public ConformanceTests(ServiceSerializer serializer) : base(serializer) { m_tests = CreateTestProvider(JsonSerializer); m_contentSerializer = HttpContentSerializer.Create(Serializer); var service = new ConformanceApiService(new ConformanceApiServiceSettings { Tests = m_tests, JsonSerializer = JsonSerializer }); var settings = new ServiceHttpHandlerSettings { ContentSerializer = m_contentSerializer }; var handler = new ConformanceApiHttpHandler(service, settings) { InnerHandler = new NotFoundHttpHandler() }; m_httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://example.com/") }; }
private string?GetAcceptedMediaType(HttpRequestMessage httpRequest, HttpContentSerializer serializer) => httpRequest.Headers.Accept .OrderByDescending(x => x.Quality) .Select(x => x.MediaType) .Where(x => x is not null)
public async Task <int> RunAsync(IReadOnlyList <string> args) { const string defaultUrl = "http://localhost:4117/"; var argsReader = new ArgsReader(args); if (argsReader.ReadFlag("?|h|help")) { throw new ArgsReaderException(""); } var serializerName = argsReader.ReadOption("serializer")?.ToLowerInvariant(); ServiceSerializer serializer = serializerName switch { null or "systemtextjson" => SystemTextJsonServiceSerializer.Instance, "newtonsoftjson" or "obsoletejson" => NewtonsoftJsonServiceSerializer.Instance, _ => throw new ArgsReaderException("Unsupported serializer."), }; var contentSerializer = HttpContentSerializer.Create(serializer); #pragma warning disable CS0618 // Type or member is obsolete if (serializerName is "obsoletejson") { contentSerializer = new JsonHttpContentSerializer(new JsonHttpContentSerializerSettings { ForceAsyncIO = true }); } #pragma warning restore CS0618 // Type or member is obsolete var jsonSerializer = serializer as JsonServiceSerializer ?? NewtonsoftJsonServiceSerializer.Instance; var tests = ConformanceTestsInfo.FromJson(m_testsJson, jsonSerializer).Tests !; var command = argsReader.ReadArgument(); if (command == "host") { var url = argsReader.ReadOption("url") ?? defaultUrl; argsReader.VerifyComplete(); var service = new ConformanceApiService( new ConformanceApiServiceSettings { Tests = tests, JsonSerializer = jsonSerializer, }); await new WebHostBuilder() .UseKestrel(options => options.AllowSynchronousIO = serializerName is "newtonsoftjson") .UseUrls(url) .Configure(app => app.Run(httpContext => HostAsync(httpContext, service, contentSerializer))) .Build() .RunAsync(); return(0); } if (command == "test") { var baseUri = new Uri(argsReader.ReadOption("url") ?? defaultUrl); var testNames = argsReader.ReadArguments(); argsReader.VerifyComplete(); var api = new HttpClientConformanceApi( new HttpClientServiceSettings { BaseUri = baseUri, ContentSerializer = contentSerializer, }); var tester = new ConformanceApiTester( new ConformanceApiTesterSettings { Tests = tests, Api = api, HttpClient = new HttpClient { BaseAddress = baseUri }, JsonSerializer = jsonSerializer, }); var results = new List <ConformanceTestResult>(); if (testNames.Count == 0) { results.AddRange((await tester.RunAllTestsAsync()).Results); } else { foreach (var testName in testNames) { var testInfo = tests.SingleOrDefault(x => x.Test == testName); if (testInfo == null) { Console.WriteLine($"Test not found: {testName}"); return(-1); } results.Add(await tester.RunTestAsync(testInfo)); } } var failureCount = 0; foreach (var result in results.Where(x => x.Status == ConformanceTestStatus.Fail)) { Console.WriteLine($"{result.TestName} fail: {result.Message}"); failureCount++; } Console.WriteLine($"{results.Count} tests: {results.Count - failureCount} passed, {failureCount} failed."); return(failureCount == 0 ? 0 : 1); } if (command == "fsd") { var outputPath = argsReader.ReadOption("output"); var shouldVerify = argsReader.ReadFlag("verify"); argsReader.VerifyComplete(); return(WriteText(path: outputPath, contents: m_fsdText, shouldVerify: shouldVerify)); } if (command == "json") { var outputPath = argsReader.ReadOption("output"); var shouldVerify = argsReader.ReadFlag("verify"); argsReader.VerifyComplete(); return(WriteText(path: outputPath, contents: m_testsJson, shouldVerify: shouldVerify)); } if (command != null) { throw new ArgsReaderException($"Invalid command: {command}"); } throw new ArgsReaderException("Missing command."); }
private async Task HostAsync(HttpContext httpContext, IConformanceApi service, HttpContentSerializer contentSerializer) { var httpRequest = httpContext.Request; var requestUrl = httpRequest.GetEncodedUrl(); var apiHandler = new ConformanceApiHttpHandler(service, new ServiceHttpHandlerSettings { ContentSerializer = contentSerializer }); var requestMessage = new HttpRequestMessage(new HttpMethod(httpRequest.Method), requestUrl) { Content = new StreamContent(httpRequest.Body), }; foreach (var header in httpRequest.Headers) { // Every header should be able to fit into one of the two header collections. // Try message.Headers first since that accepts more of them. if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.AsEnumerable())) { requestMessage.Content.Headers.TryAddWithoutValidation(header.Key, header.Value.AsEnumerable()); } } HttpResponseMessage?responseMessage = null; ServiceErrorDto? error = null; try { responseMessage = await apiHandler.TryHandleHttpRequestAsync(requestMessage, httpContext.RequestAborted).ConfigureAwait(false); if (responseMessage == null) { error = ServiceErrors.CreateInvalidRequest($"Test not found for {httpRequest.Method} {requestUrl}"); } } catch (Exception exception) { error = ServiceErrorUtility.CreateInternalErrorForException(exception); } if (error != null) { var statusCode = HttpServiceErrors.TryGetHttpStatusCode(error.Code) ?? HttpStatusCode.InternalServerError; responseMessage = new HttpResponseMessage(statusCode) { Content = contentSerializer.CreateHttpContent(error) }; } if (responseMessage != null) { using (responseMessage) { var response = httpContext.Response; response.StatusCode = (int)responseMessage.StatusCode; var responseHeaders = responseMessage.Headers; // Ignore the Transfer-Encoding header if it is just "chunked". // We let the host decide about whether the response should be chunked or not. if (responseHeaders.TransferEncodingChunked == true && responseHeaders.TransferEncoding.Count == 1) { responseHeaders.TransferEncoding.Clear(); } foreach (var header in responseHeaders) { response.Headers.Append(header.Key, header.Value.ToArray()); } // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (responseMessage.Content != null) { var contentHeaders = responseMessage.Content.Headers; // Copy the response content headers only after ensuring they are complete. // We ask for Content-Length first because HttpContent lazily computes this // and only afterwards writes the value into the content headers. _ = contentHeaders.ContentLength; foreach (var header in contentHeaders) { response.Headers.Append(header.Key, header.Value.ToArray()); } await responseMessage.Content.CopyToAsync(response.Body).ConfigureAwait(false); } } } }