public void HeadersParsing() { const string request = "GET / HTTP/1.1\r\n" + "Host: localhost:80\r\n" + "User-Agent: xunit\r\n" + "Connection: close\r\n" + "X-Multiple-Header: value1, value2\r\n" + "\r\n"; // five lines total byte[] requestBytes = Encoding.UTF8.GetBytes(request); int position = 0; using (var stream = new MemoryStream()) { stream.Write(requestBytes, 0, requestBytes.Length); stream.Seek(0, SeekOrigin.Begin); var rawHeaders = Http11Helper.ReadHeaders(stream); Assert.Equal(rawHeaders.Length, 5); string[] splittedRequestString = rawHeaders[0].Split(' '); Assert.Equal(splittedRequestString[0], "GET"); Assert.Equal(splittedRequestString[1], "/"); Assert.Equal(splittedRequestString[2], "HTTP/1.1"); var headers = Http11Helper.ParseHeaders(rawHeaders.Skip(1).ToArray()); Assert.Equal(headers.Count, 4); Assert.Contains("Host", headers.Keys); Assert.Contains("User-Agent", headers.Keys); Assert.Contains("Connection", headers.Keys); Assert.Contains("X-Multiple-Header", headers.Keys); Assert.Equal(headers["Host"].Length, 1); Assert.Equal(headers["User-Agent"].Length, 1); Assert.Equal(headers["Connection"].Length, 1); Assert.Equal(headers["X-Multiple-Header"].Length, 2); Assert.Equal(headers["Host"][0], "localhost:80"); Assert.Equal(headers["User-Agent"][0], "xunit"); Assert.Equal(headers["Connection"][0], "close"); Assert.Equal(headers["X-Multiple-Header"][0], "value1"); Assert.Equal(headers["X-Multiple-Header"][1], "value2"); } }
public void UpgradeByHttp11() { var requestStr = ConfigurationManager.AppSettings["smallTestFile"]; Uri uri; Uri.TryCreate(ConfigurationManager.AppSettings["unsecureAddress"] + requestStr, UriKind.Absolute, out uri); bool finalFrameReceived = false; var responseBody = new StringBuilder(); var finalFrameReceivedRaisedEvent = new ManualResetEvent(false); var clientStream = TestHelpers.GetHandshakedStream(uri); var mockedAdapter = new Mock <Http2ClientMessageHandler>(clientStream, ConnectionEnd.Client, clientStream is SslStream, new CancellationToken()) { CallBase = true }; var adapter = mockedAdapter.Object; mockedAdapter.Protected().Setup("ProcessIncomingData", ItExpr.IsAny <Http2Stream>(), ItExpr.IsAny <Frame>()) .Callback <Http2Stream, Frame>((stream, frame) => { bool isFin; do { var dataFrame = frame as DataFrame; responseBody.Append(Encoding.UTF8.GetString( dataFrame.Payload.Array.Skip(dataFrame.Payload.Offset) .Take(dataFrame.Payload.Count) .ToArray())); isFin = dataFrame.IsEndStream; } while (!isFin && stream.ReceivedDataAmount > 0); if (isFin) { finalFrameReceived = true; finalFrameReceivedRaisedEvent.Set(); } }); // process http/1.1 headers var http11Headers = "GET " + uri.AbsolutePath + " HTTP/1.1\r\n" + "Host: " + uri.Host + "\r\n" + "Connection: Upgrade, HTTP2-Settings\r\n" + "Upgrade: " + Protocols.Http2NoTls + "\r\n" + "HTTP2-Settings: \r\n" + // TODO send any valid settings "\r\n"; clientStream.Write(Encoding.UTF8.GetBytes(http11Headers)); clientStream.Flush(); try { var response = Http11Helper.ReadHeaders(clientStream); Assert.Equal( "HTTP/1.1 " + StatusCode.Code101SwitchingProtocols + " " + StatusCode.Reason101SwitchingProtocols, response[0]); var headers = Http11Helper.ParseHeaders(response.Skip(1)); Assert.Contains("Connection", headers.Keys); Assert.Equal("Upgrade", headers["Connection"][0]); Assert.Contains("Upgrade", headers.Keys); Assert.Equal(Protocols.Http2NoTls, headers["Upgrade"][0]); adapter.StartSessionAsync(); // there are http2 frames after upgrade headers - we don't need to send request explicitly finalFrameReceivedRaisedEvent.WaitOne(10000); Assert.True(finalFrameReceived); Assert.Equal(TestHelpers.FileContentSimpleTest, responseBody.ToString()); } finally { adapter.Dispose(); } }
/// <summary> /// Processes incoming request. /// </summary> public async void ProcessRequest() { try { // invalid connection, skip if (!_client.CanRead) { return; } var rawHeaders = Http11Helper.ReadHeaders(_client); Http2Logger.LogDebug("Http1.1 Protocol Handler. Process request " + string.Join(" ", rawHeaders)); // invalid connection, skip if (rawHeaders == null || rawHeaders.Length == 0) { return; } // headers[0] contains METHOD, URI and VERSION like "GET /api/values/1 HTTP/1.1" var headers = Http11Helper.ParseHeaders(rawHeaders.Skip(1).ToArray()); // client MUST include Host header due to HTTP/1.1 spec if (!headers.ContainsKey("Host")) { throw new ApplicationException("Host header is missing"); } // parse request parameters: method, path, host, etc var splittedRequestString = rawHeaders[0].Split(' '); var method = splittedRequestString[0]; if (!IsMethodSupported(method)) { throw new NotSupportedException(method + " method is not currently supported via HTTP/1.1"); } var scheme = _protocol == SslProtocols.None ? Uri.UriSchemeHttp : Uri.UriSchemeHttps; var path = splittedRequestString[1]; // main OWIN environment components // OWIN request and response below shares the same environment dictionary instance _environment = CreateOwinEnvironment(method, scheme, "", path, headers); _request = new OwinRequest(_environment); _response = new OwinResponse(_environment); // we may need to populate additional fields if request supports UPGRADE AddOpaqueUpgradeIfNeeded(); await _next.Invoke(new OwinContext(_environment)); if (_opaqueCallback == null) { EndResponse(); } else { // do not close connection EndResponse(false); var opaqueEnvironment = CreateOpaqueEnvironment(); await _opaqueCallback(new OwinContext(opaqueEnvironment)); } } catch (Exception ex) { Http2Logger.LogError(ex.Message); EndResponse(ex); Http2Logger.LogDebug("Closing connection"); _client.Close(); } }