/// <summary>
        /// Adopts protocol terms into owin environment.
        /// </summary>
        /// <param name="headers">The headers.</param>
        /// <returns></returns>
        private OwinContext PopulateEnvironment(HeadersList headers)
        {
            var owinContext = new OwinContext();

            var headersAsDict = headers.ToDictionary(header => header.Key, header => new[] {header.Value}, StringComparer.OrdinalIgnoreCase);

            owinContext.Environment[CommonOwinKeys.RequestHeaders] = headersAsDict;
            owinContext.Environment[CommonOwinKeys.ResponseHeaders] = new Dictionary<string, string[]>();

            var owinRequest = owinContext.Request;
            var owinResponse = owinContext.Response;

            owinRequest.Method = headers.GetValue(CommonHeaders.Method);
            owinRequest.Path = headers.GetValue(CommonHeaders.Path);
            owinRequest.CallCancelled = CancellationToken.None;

            owinRequest.Host = headers.GetValue(CommonHeaders.Host);
            owinRequest.PathBase = String.Empty;
            owinRequest.QueryString = String.Empty;
            owinRequest.Body = new MemoryStream();
            owinRequest.Protocol = Protocols.Http2;
            owinRequest.Scheme = headers.GetValue(CommonHeaders.Scheme) == Uri.UriSchemeHttp ? Uri.UriSchemeHttp : Uri.UriSchemeHttps;
            owinRequest.RemoteIpAddress = _transportInfo.RemoteIpAddress;
            owinRequest.RemotePort = Convert.ToInt32(_transportInfo.RemotePort);
            owinRequest.LocalIpAddress = _transportInfo.LocalIpAddress;
            owinRequest.LocalPort = _transportInfo.LocalPort;

            owinResponse.Body = new ResponseStream{Capacity = 16384};

            return owinContext;
        }
Пример #2
0
        public void CompressionSuccessful()
        {
            var clientHeaders = new HeadersList
            {
                new KeyValuePair <string, string>(":method", "get"),
                new KeyValuePair <string, string>(":path", "/test.txt"),
                new KeyValuePair <string, string>(":version", Protocols.Http2),
                new KeyValuePair <string, string>(":host", "localhost"),
                new KeyValuePair <string, string>(":scheme", "http"),
            };
            var clientCompressor   = new CompressionProcessor(ConnectionEnd.Client);
            var serverDecompressor = new CompressionProcessor(ConnectionEnd.Server);

            var serializedHeaders   = clientCompressor.Compress(clientHeaders);
            var decompressedHeaders = new HeadersList(serverDecompressor.Decompress(serializedHeaders));

            foreach (var t in clientHeaders)
            {
                Assert.Equal(decompressedHeaders.GetValue(t.Key), t.Value);
            }

            var serverHeaders = new HeadersList
            {
                new KeyValuePair <string, string>(":status", StatusCode.Code200Ok.ToString()),
            };
            var serverCompressor   = new CompressionProcessor(ConnectionEnd.Server);
            var clientDecompressor = new CompressionProcessor(ConnectionEnd.Client);

            serializedHeaders   = serverCompressor.Compress(serverHeaders);
            decompressedHeaders = new HeadersList(clientCompressor.Decompress(serializedHeaders));

            foreach (var t in serverHeaders)
            {
                Assert.Equal(decompressedHeaders.GetValue(t.Key), t.Value);
            }
        }
Пример #3
0
        /// <summary>
        /// Dispatches the incoming frame.
        /// </summary>
        /// <param name="frame">The frame.</param>
        /// <exception cref="System.NotImplementedException"></exception>
        private void DispatchIncomingFrame(Frame frame)
        {
            Http2Stream stream = null;

            //Spec 03 tells that frame with continues flag MUST be followed by a frame with the same type
            //and the same stread id.
            if (_toBeContinuedHeaders.Count != 0)
            {
                if (_toBeContinuedFrame.FrameType != frame.FrameType
                    || _toBeContinuedFrame.StreamId != frame.StreamId)
                {
                    //If not, we must close the session.
                    Dispose();
                    return;
                }
            }

            try
            {
                switch (frame.FrameType)
                {
                    case FrameType.Headers:
                        Http2Logger.LogDebug("New headers with id = " + frame.StreamId);
                        var headersFrame = (HeadersFrame)frame;
                        var serializedHeaders = new byte[headersFrame.CompressedHeaders.Count];

                        Buffer.BlockCopy(headersFrame.CompressedHeaders.Array,
                                         headersFrame.CompressedHeaders.Offset,
                                         serializedHeaders, 0, serializedHeaders.Length);

                        var decompressedHeaders = _comprProc.Decompress(serializedHeaders);
                        var headers = new HeadersList(decompressedHeaders);

                        if (!headersFrame.IsEndHeaders)
                        {
                            _toBeContinuedHeaders.AddRange(decompressedHeaders);
                            _toBeContinuedFrame = headersFrame;
                            break;
                        }

                        headers.AddRange(_toBeContinuedHeaders);
                        _toBeContinuedHeaders.Clear();
                        _toBeContinuedFrame = null;
                        headersFrame.Headers.AddRange(headers);
                        foreach (var header in headers)
                        {
                            Http2Logger.LogDebug("Stream {0} header: {1}={2}", frame.StreamId, header.Key, header.Value);
                        }

                        stream = GetStream(headersFrame.StreamId);

                        if (stream == null)
                        {
                            if (_ourEnd == ConnectionEnd.Server)
                            {
                                string path = headers.GetValue(":path");
                                if (path == null)
                                {
                                    path = _handshakeHeaders.ContainsKey(":path")
                                               ? _handshakeHeaders[":path"]
                                               : @"\index.html";
                                    headers.Add(new KeyValuePair<string, string>(":path", path));
                                }
                            }
                            else
                            {
                                headers.AddRange(_handshakeHeaders);
                            }
                            stream = CreateStream(headers, frame.StreamId);
                        }

                        break;

                    case FrameType.Priority:
                        var priorityFrame = (PriorityFrame)frame;
                        Http2Logger.LogDebug("Priority frame. StreamId: {0} Priority: {1}", priorityFrame.StreamId, priorityFrame.Priority);
                        stream = GetStream(priorityFrame.StreamId);
                        if (_usePriorities)
                        {
                            stream.Priority = priorityFrame.Priority;
                        }
                        break;

                    case FrameType.RstStream:
                        var resetFrame = (RstStreamFrame)frame;
                        stream = GetStream(resetFrame.StreamId);

                        if (stream != null)
                        {
                            Http2Logger.LogDebug("RST frame with code " + resetFrame.StatusCode);
                            stream.Dispose();
                        }
                        break;
                    case FrameType.Data:
                        var dataFrame = (DataFrame)frame;
                        Http2Logger.LogDebug("Data frame. StreamId:{0} Length:{1}", dataFrame.StreamId, dataFrame.FrameLength);
                        stream = GetStream(dataFrame.StreamId);

                        //Aggressive window update
                        if (stream != null && stream.IsFlowControlEnabled)
                        {
                            stream.WriteWindowUpdate(InitialWindowSize);
                        }
                        break;
                    case FrameType.Ping:
                        var pingFrame = (PingFrame)frame;
                        Http2Logger.LogDebug("Ping frame with StreamId:{0} Payload:{1}", pingFrame.StreamId, pingFrame.Payload.Count);

                        if (pingFrame.FrameLength != PingFrame.PayloadLength)
                        {
                            throw new ProtocolError(ResetStatusCode.ProtocolError, "Ping payload size is not equal to 8");
                        }

                        if (pingFrame.IsPong)
                        {
                            _wasPingReceived = true;
                            _pingReceived.Set();
                        }
                        else
                        {
                            var pongFrame = new PingFrame(true, pingFrame.Payload.ToArray());
                            _writeQueue.WriteFrame(pongFrame);
                        }
                        break;
                    case FrameType.Settings:
                        //Not first frame in the session.
                        //Client initiates connection and sends settings before request.
                        //It means that if server sent settings before it will not be a first frame,
                        //because client initiates connection.
                        if (_ourEnd == ConnectionEnd.Server && !_wasSettingsReceived
                            && (ActiveStreams.Count > 0))
                        {
                            Dispose();
                            return;
                        }

                        var settingFrame = (SettingsFrame)frame;
                        Http2Logger.LogDebug("Settings frame. Entry count: {0} StreamId: {1}", settingFrame.EntryCount, settingFrame.StreamId);
                        _wasSettingsReceived = true;
                        _settingsManager.ProcessSettings(settingFrame, this, _flowControlManager);

                        if (_ourEnd == ConnectionEnd.Server && _sessionSocket.SecureProtocol == SecureProtocol.None)
                        {
                            //The HTTP/1.1 request that is sent prior to upgrade is associated with
                            //stream 1 and is assigned the highest possible priority.  Stream 1 is
                            //implicitly half closed from the client toward the server, since the
                            //request is completed as an HTTP/1.1 request.  After commencing the
                            //HTTP/2.0 connection, stream 1 is used for the response.
                            stream = CreateStream(Priority.Pri0);
                            stream.EndStreamReceived = true;
                            stream.Headers.Add(new KeyValuePair<string, string>(":method", _handshakeHeaders[":method"]));
                            stream.Headers.Add(new KeyValuePair<string, string>(":path", _handshakeHeaders[":path"]));
                            OnFrameReceived(this, new FrameReceivedEventArgs(stream, new HeadersFrame(stream.Id, false)));
                        }

                        break;
                    case FrameType.WindowUpdate:
                        if (_useFlowControl)
                        {
                            var windowFrame = (WindowUpdateFrame)frame;
                            Http2Logger.LogDebug("WindowUpdate frame. Delta: {0} StreamId: {1}", windowFrame.Delta, windowFrame.StreamId);
                            stream = GetStream(windowFrame.StreamId);

                            if (stream != null)
                            {
                                stream.UpdateWindowSize(windowFrame.Delta);
                                stream.PumpUnshippedFrames();
                            }
                        }
                        break;

                    case FrameType.GoAway:
                        _goAwayReceived = true;
                        Http2Logger.LogDebug("GoAway frame received");
                        Dispose();
                        break;
                    default:
                        throw new NotImplementedException(frame.FrameType.ToString());
                }

                if (stream != null && frame is IEndStreamFrame && ((IEndStreamFrame)frame).IsEndStream)
                {
                    //Tell the stream that it was the last frame
                    Http2Logger.LogDebug("Final frame received for stream with id = " + stream.Id);
                    stream.EndStreamReceived = true;
                }

                if (stream != null && OnFrameReceived != null)
                {
                    OnFrameReceived(this, new FrameReceivedEventArgs(stream, frame));
                }
            }

            //Frame came for already closed stream. Ignore it.
            //Spec:
            //An endpoint that sends RST_STREAM MUST ignore
            //frames that it receives on closed streams if it sends RST_STREAM.
            //
            //An endpoint MUST NOT send frames on a closed stream.  An endpoint
            //that receives a frame after receiving a RST_STREAM or a frame
            //containing a END_STREAM flag on that stream MUST treat that as a
            //stream error (Section 5.4.2) of type PROTOCOL_ERROR.
            catch (Http2StreamNotFoundException)
            {
                if (stream != null)
                {
                    stream.WriteRst(ResetStatusCode.ProtocolError);
                }
                else
                {
                    //GoAway?
                }
            }
            catch (CompressionError ex)
            {
                //The endpoint is unable to maintain the compression context for the connection.
                Http2Logger.LogError("Compression error occurred: " + ex.Message);
                Close(ResetStatusCode.CompressionError);
            }
            catch (ProtocolError pEx)
            {
                Http2Logger.LogError("Protocol error occurred: " + pEx.Message);
                Close(pEx.Code);
            }
        }
Пример #4
0
        /// <summary>
        /// Sends the headers with request headers.
        /// </summary>
        /// <param name="pairs">The header pairs.</param>
        /// <param name="priority">The stream priority.</param>
        /// <param name="isEndStream">True if initial headers+priority is also the final frame from endpoint.</param>
        public void SendRequest(HeadersList pairs, int priority, bool isEndStream)
        {
            if (_ourEnd == ConnectionEnd.Server)
                throw new ProtocolError(ResetStatusCode.ProtocolError, "Server should not initiate request");

            if (pairs == null)
                throw new ArgumentNullException("pairs is null");

            if (priority < 0 || priority > Constants.MaxPriority)
                throw new ArgumentOutOfRangeException("priority is not between 0 and MaxPriority");

            var path = pairs.GetValue(CommonHeaders.Path);

            if (path == null)
                throw new ProtocolError(ResetStatusCode.ProtocolError, "Invalid request ex");

            //09 -> 8.2.2.  Push Responses
            //Once a client receives a PUSH_PROMISE frame and chooses to accept the
            //pushed resource, the client SHOULD NOT issue any requests for the
            //promised resource until after the promised stream has closed.
            if (_promisedResources.ContainsValue(path))
                throw new ProtocolError(ResetStatusCode.ProtocolError, "Resource has been promised. Client should not request it.");

            var stream = CreateStream(priority);

            stream.WriteHeadersFrame(pairs, isEndStream, true);

            var streamSequence = _headersSequences.Find(stream.Id);
            streamSequence.AddHeaders(new HeadersFrame(stream.Id, stream.Priority) { Headers = pairs });

            if (OnRequestSent != null)
            {
                OnRequestSent(this, new RequestSentEventArgs(stream));
            }
        }
Пример #5
0
        public void HeadersCompression()
        {
            var clientHeaders = new HeadersList
            {
                new KeyValuePair <string, string>(":method", "get"),
                new KeyValuePair <string, string>(":path", "/Y3A9NTcuNjE2NjY1fjM5Ljg2NjY2NSZsdmw9NyZzdHk9ciZxPVlhcm9zbGF2bA=="),
                new KeyValuePair <string, string>(":version", Protocols.Http2),
                new KeyValuePair <string, string>(":host", "localhost"),
                new KeyValuePair <string, string>(":scheme", "https"),
            };
            var clientCompressionProc = new CompressionProcessor();
            var serverCompressionProc = new CompressionProcessor();

            var serializedHeaders   = clientCompressionProc.Compress(clientHeaders);
            var decompressedHeaders = new HeadersList(serverCompressionProc.Decompress(serializedHeaders));

            foreach (var t in clientHeaders)
            {
                Assert.Equal(decompressedHeaders.GetValue(t.Key), t.Value);
            }

            var serverHeaders = new HeadersList
            {
                new KeyValuePair <string, string>(":method", "get"),
                new KeyValuePair <string, string>(":path", "/simpleTest.txt"),
                new KeyValuePair <string, string>(":version", Protocols.Http2),
                new KeyValuePair <string, string>(":host", "localhost"),
                new KeyValuePair <string, string>(":scheme", "https"),
            };

            serializedHeaders   = serverCompressionProc.Compress(serverHeaders);
            decompressedHeaders = new HeadersList(clientCompressionProc.Decompress(serializedHeaders));

            foreach (var t in serverHeaders)
            {
                Assert.Equal(decompressedHeaders.GetValue(t.Key), t.Value);
            }

            serverHeaders = new HeadersList
            {
                new KeyValuePair <string, string>(":status", StatusCode.Code404NotFound.ToString(CultureInfo.InvariantCulture)),
            };

            serializedHeaders   = serverCompressionProc.Compress(serverHeaders);
            decompressedHeaders = new HeadersList(clientCompressionProc.Decompress(serializedHeaders));

            foreach (var t in serverHeaders)
            {
                Assert.Equal(decompressedHeaders.GetValue(t.Key), t.Value);
            }

            serverHeaders = new HeadersList
            {
                new KeyValuePair <string, string>("content-type", "text/plain"),
                new KeyValuePair <string, string>("last-modified", "Wed, 23 Oct 2013 21:32:06 GMT"),
                new KeyValuePair <string, string>("etag", "1cedo15cb041fc1"),
                new KeyValuePair <string, string>("content-length", "749761"),
                new KeyValuePair <string, string>(":status", StatusCode.Code200Ok.ToString(CultureInfo.InvariantCulture)),
            };

            serializedHeaders   = serverCompressionProc.Compress(serverHeaders);
            decompressedHeaders = new HeadersList(clientCompressionProc.Decompress(serializedHeaders));

            foreach (var t in serverHeaders)
            {
                Assert.Equal(decompressedHeaders.GetValue(t.Key), t.Value);
            }

            clientHeaders = new HeadersList
            {
                new KeyValuePair <string, string>(":method", "get"),
                new KeyValuePair <string, string>(":path", "/index.html"),
                new KeyValuePair <string, string>(":version", Protocols.Http2),
                new KeyValuePair <string, string>(":host", "localhost"),
                new KeyValuePair <string, string>(":scheme", "https"),
            };

            serializedHeaders   = clientCompressionProc.Compress(clientHeaders);
            decompressedHeaders = new HeadersList(serverCompressionProc.Decompress(serializedHeaders));

            foreach (var t in clientHeaders)
            {
                Assert.Equal(decompressedHeaders.GetValue(t.Key), t.Value);
            }
        }
        private async Task SendResponse(Http2Stream stream, IOwinContext context)
        {
            try
            {
                var isFirstWrite = true;
                var response = context.Response;
                var respBody = response.Body as ResponseStream;
                HeadersList responseHeaders = null;

                if (respBody == null)
                {
                    stream.WriteRst(ResetStatusCode.InternalError);
                    return;
                }

                int contentLen = 0;
                int read = 0;

                respBody.OnDataWritten += (sender, args) =>
                    {
                        if (isFirstWrite)
                        {
                            Http2Logger.LogDebug("Transfer begin");
                            if (response.Headers != null)
                            {
                                responseHeaders = new HeadersList(response.Headers);
                                contentLen = int.Parse(responseHeaders.GetValue(CommonHeaders.ContentLength));
                            }
                            WriteStatus(stream, response.StatusCode, response.StatusCode != StatusCode.Code200Ok, responseHeaders);
                        }
                        isFirstWrite = false;

                        if (read < contentLen)
                        {
                            var temp = new byte[args.Count];

                            respBody.Seek(0, SeekOrigin.Begin);
                            int tmpRead = respBody.Read(temp, 0, temp.Length);
                            respBody.Seek(0, SeekOrigin.Begin);

                            Debug.Assert(tmpRead > 0);

                            var readBytes = new byte[tmpRead];
                            Buffer.BlockCopy(temp, 0, readBytes, 0, tmpRead);

                            read += tmpRead;
                            SendDataTo(stream, readBytes, read == contentLen);
                        }
                    };

                await _next(context.Environment);

                //Handle file not found case
                if (response.StatusCode == StatusCode.Code404NotFound)
                {
                    WriteStatus(stream, response.StatusCode, response.StatusCode != StatusCode.Code200Ok, responseHeaders);
                }

                Http2Logger.LogDebug("Transfer end");
            }
            catch (Exception ex)
            {   
                EndResponse(stream, ex);;
            }
        }