Beispiel #1
0
        /// <summary>
        /// Creates new http2 stream.
        /// </summary>
        /// <param name="priority">The stream priority.</param>
        /// <returns></returns>
        /// <exception cref="System.InvalidOperationException">Thrown when trying to create more streams than allowed by the remote side</exception>
        private Http2Stream CreateStream(Priority priority)
        {
            if (ActiveStreams.GetOpenedStreamsBy(_ourEnd) + 1 > RemoteMaxConcurrentStreams)
            {
                Dispose();
                throw new InvalidOperationException("Trying to create more streams than allowed by the remote side!");
            }

            var id = GetNextId();

            if (_usePriorities)
            {
                ActiveStreams[id] = new Http2Stream(id, _writeQueue, _flowControlManager, _comprProc, priority);
            }
            else
            {
                ActiveStreams[id] = new Http2Stream(id, _writeQueue, _flowControlManager, _comprProc);
            }

            ActiveStreams[id].OnClose += (o, args) =>
            {
                if (!ActiveStreams.Remove(ActiveStreams[args.Id]))
                {
                    throw new ArgumentException("Can't remove stream from ActiveStreams.");
                }
            };

            ActiveStreams[id].OnFrameSent += (o, args) =>
            {
                if (OnFrameSent != null)
                {
                    OnFrameSent(o, args);
                }
            };

            return(ActiveStreams[id]);
        }
Beispiel #2
0
        /// <summary>
        /// Creates new http2 stream.
        /// </summary>
        /// <param name="priority">The stream priority.</param>
        /// <returns></returns>
        /// <exception cref="System.InvalidOperationException">Thrown when trying to create more streams than allowed by the remote side</exception>
        private Http2Stream CreateStream(Priority priority)
        {
            if (ActiveStreams.GetOpenedStreamsBy(_ourEnd) + 1 > RemoteMaxConcurrentStreams)
            {
                Dispose();
                throw new InvalidOperationException("Trying to create more streams than allowed!");
            }

            var id = GetNextId();
            if (_usePriorities)
            {
                ActiveStreams[id] = new Http2Stream(id, _writeQueue, _flowControlManager, _comprProc, priority);
            }
            else
            {
                ActiveStreams[id] = new Http2Stream(id, _writeQueue, _flowControlManager, _comprProc);
            }

            ActiveStreams[id].OnClose += (o, args) =>
                {
                    if (!ActiveStreams.Remove(ActiveStreams[args.Id]))
                    {
                        throw new ArgumentException("Can't remove stream from ActiveStreams.");
                    }
                };

            ActiveStreams[id].OnFrameSent += (o, args) =>
                {
                    if (OnFrameSent != null)
                    {
                        OnFrameSent(o, args);
                    }
                };

            return ActiveStreams[id];
        }
Beispiel #3
0
        /// <summary>
        /// Creates stream.
        /// </summary>
        /// <param name="headers"></param>
        /// <param name="streamId"></param>
        /// <returns></returns>
        private Http2Stream CreateStream(HeadersList headers, int streamId)
        {
            if (ActiveStreams.GetOpenedStreamsBy(_remoteEnd) + 1 > OurMaxConcurrentStreams)
            {
                //Remote side tries to open more streams than allowed
                Dispose();
                throw new InvalidOperationException("Trying to create more streams than allowed!");
            }

            var stream = new Http2Stream(headers, streamId,
                                      _writeQueue, _flowControlManager,
                                      _comprProc);

            ActiveStreams[stream.Id] = stream;

            stream.OnClose += (o, args) =>
            {
                if (!ActiveStreams.Remove(ActiveStreams[args.Id]))
                {
                    throw new ArgumentException("Cant remove stream from ActiveStreams");
                }
            };

            return stream;
        }
        private void SaveDataFrame(Http2Stream stream, DataFrame dataFrame)
        {
            lock (_writeLock)
            {
                string originalPath = stream.Headers.GetValue(":path".ToLower()); 
                //If user sets the empty file in get command we return notFound webpage
                string fileName = string.IsNullOrEmpty(Path.GetFileName(originalPath)) ? Index : Path.GetFileName(originalPath);
                string path = Path.Combine(AssemblyPath, fileName);

                try
                {
                        _fileHelper.SaveToFile(dataFrame.Data.Array, dataFrame.Data.Offset, dataFrame.Data.Count,
                                           path, stream.ReceivedDataAmount != 0);
                }
                catch (IOException)
                {
                    Http2Logger.LogError("File is still downloading. Repeat request later");
                    stream.WriteDataFrame(new byte[0], true);
                    stream.Dispose();
                }

                stream.ReceivedDataAmount += dataFrame.FrameLength;

                if (dataFrame.IsEndStream)
                {
                    if (!stream.EndStreamSent)
                    {
                        //send terminator
                        stream.WriteDataFrame(new byte[0], true);
                        Http2Logger.LogConsole("Terminator was sent");
                    }
                    _fileHelper.RemoveStream(path);
                    Http2Logger.LogConsole("Bytes received " + stream.ReceivedDataAmount);
#if DEBUG
                    const string wayToServerRoot1 = @"..\..\..\..\..\Drop\Root";
                    const string wayToServerRoot2 = @".\Root";
                    var areFilesEqual = _fileHelper.CompareFiles(path, wayToServerRoot1 + originalPath) ||
                                        _fileHelper.CompareFiles(path, wayToServerRoot2 + originalPath);
                    if (!areFilesEqual)
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Http2Logger.LogError("Files are NOT EQUAL!");
                    }
                    else
                    {
                        Console.ForegroundColor = ConsoleColor.Green;
                        Http2Logger.LogConsole("Files are EQUAL!");
                    }
                    Console.ForegroundColor = ConsoleColor.Gray;
#endif
                }
            }
        }
        private void SendDataTo(Http2Stream stream, byte[] binaryData)
        {
            int i = 0;

            Http2Logger.LogConsole("Transfer begin");

            do
            {
                bool isLastData = binaryData.Length - i < Constants.MaxDataFrameContentSize;

                int chunkSize = stream.WindowSize > 0
                                    ? MathEx.Min(binaryData.Length - i, Constants.MaxDataFrameContentSize,
                                                 stream.WindowSize)
                                    : MathEx.Min(binaryData.Length - i, Constants.MaxDataFrameContentSize);

                var chunk = new byte[chunkSize];
                Buffer.BlockCopy(binaryData, i, chunk, 0, chunk.Length);

                stream.WriteDataFrame(chunk, isLastData);

                i += chunkSize;
            } while (binaryData.Length > i);

            //It was not send exactly. Some of the data frames could be pushed to the unshipped frames collection
            Http2Logger.LogConsole("File sent: " + stream.Headers.GetValue(":path"));
        }
 public FrameReceivedEventArgs(Http2Stream stream, Frame frame)
 {
     Stream = stream;
     Frame = frame;
 }
        private void SaveDataFrame(Http2Stream stream, DataFrame dataFrame)
        {
            lock (_writeLock)
            {
                string path = stream.Headers.GetValue(":path".ToLower());

                try
                {
                    string pathToSave = AssemblyPath + @"\Root" + path;
                    if (!Directory.Exists(Path.GetDirectoryName(pathToSave)))
                    {
                        throw new DirectoryNotFoundException("Access denied");
                    }
                     _fileHelper.SaveToFile(dataFrame.Data.Array, dataFrame.Data.Offset, dataFrame.Data.Count,
                                               pathToSave, stream.ReceivedDataAmount != 0);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    stream.WriteDataFrame(new byte[0], true);
                    stream.Dispose();
                }

                stream.ReceivedDataAmount += dataFrame.FrameLength;

                if (dataFrame.IsEndStream)
                {
                    if (!stream.EndStreamSent)
                    {
                        //send terminator
                        stream.WriteDataFrame(new byte[0], true);
                        Console.WriteLine("Terminator was sent");
                    }
                    _fileHelper.RemoveStream(AssemblyPath + @"\Root" + path);
                }
            }
        }
Beispiel #8
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 != null)
            {
                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 = decompressedHeaders;
                        _toBeContinuedFrame   = headersFrame;
                        break;
                    }

                    if (_toBeContinuedHeaders != null)
                    {
                        headers.AddRange(_toBeContinuedHeaders);
                    }

                    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)
                    {
                        // new stream
                        if (ActiveStreams.GetOpenedStreamsBy(_remoteEnd) + 1 > OurMaxConcurrentStreams)
                        {
                            //Remote side tries to open more streams than allowed
                            Dispose();
                            return;
                        }

                        string path = headers.GetValue(":path");

                        if (path == null)
                        {
                            path = _handshakeHeaders.ContainsKey(":path") ? _handshakeHeaders[":path"] : @"\index.html";
                            headers.Add(new KeyValuePair <string, string>(":path", path));
                        }

                        stream = new Http2Stream(headers, headersFrame.StreamId,
                                                 _writeQueue, _flowControlManager,
                                                 _comprProc);

                        ActiveStreams[stream.Id] = stream;

                        stream.OnClose += (o, args) =>
                        {
                            if (!ActiveStreams.Remove(ActiveStreams[args.Id]))
                            {
                                throw new ArgumentException("Cant remove stream from ActiveStreams");
                            }
                        };

                        _toBeContinuedFrame   = null;
                        _toBeContinuedHeaders = null;
                    }

                    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.IsFlowControlEnabled)
                    {
                        stream.WriteWindowUpdate(2000000);
                    }
                    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);
                    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 occured: " + ex.Message);
                Close(ResetStatusCode.CompressionError);
            }
            catch (ProtocolError pEx)
            {
                Http2Logger.LogError("Protocol error occured: " + pEx.Message);
                Close(pEx.Code);
            }
        }
 private void WriteStatus(Http2Stream stream, int statusCode, bool final)
 {
     var headers = new HeadersList
     {
         new KeyValuePair<string, string>(":status", statusCode.ToString()),
     };
     stream.WriteHeadersFrame(headers, final, true);
 }
Beispiel #10
0
 public FrameReceivedEventArgs(Http2Stream stream, Frame frame)
 {
     Stream = stream;
     Frame  = frame;
 }
        private void SendFile(string path, Http2Stream stream)
        {
            // check if root is requested, in which case send index.html
            if (string.IsNullOrEmpty(path))
                path = IndexHtml;

            byte[] binary = _fileHelper.GetFile(path);
            WriteStatus(stream, StatusCode.Code200Ok, false);
            SendDataTo(stream, binary);
            Http2Logger.LogDebug("File sent: " + path);
        }
        private void SendDataTo(Http2Stream stream, byte[] binaryData)
        {
            int i = 0;

            Http2Logger.LogDebug("Transfer begin");

            do
            {
                bool isLastData = binaryData.Length - i < Constants.MaxDataFrameContentSize;

                int chunkSize = stream.WindowSize > 0
                                    ? MathEx.Min(binaryData.Length - i, Constants.MaxDataFrameContentSize,
                                                 stream.WindowSize)
                                    : MathEx.Min(binaryData.Length - i, Constants.MaxDataFrameContentSize);

                var chunk = new byte[chunkSize];
                Buffer.BlockCopy(binaryData, i, chunk, 0, chunk.Length);

                stream.WriteDataFrame(chunk, isLastData);

                i += chunkSize;
            } while (binaryData.Length > i);
        }
Beispiel #13
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 != null)
            {
                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:
                        Console.WriteLine("New headers with id = " + frame.StreamId);
                        var headersFrame = (Headers) 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, frame.StreamId % 2 != 0);
                        var headers = decompressedHeaders;

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

                        if (_toBeContinuedHeaders != null)
                        {
                            headers.AddRange(_toBeContinuedHeaders);
                        }

                        //Remote side tries to open more streams than allowed
                        if (ActiveStreams.GetOpenedStreamsBy(_remoteEnd) + 1 > OurMaxConcurrentStreams)
                        {
                            Dispose();
                            return;
                        }

                        string path;
                        try
                        {
                            path = headers.GetValue(":path");
                        }
                        catch (KeyNotFoundException)
                        {
                            path = !_handshakeHeaders.ContainsKey(":path") ? _handshakeHeaders[":path"] : @"\index.html";
                            headers.Add(new Tuple<string, string, IAdditionalHeaderInfo>(":path", path, null));
                        }

                        stream = new Http2Stream(headers, headersFrame.StreamId,
                                                  _writeQueue, _flowControlManager,
                                                  _comprProc);

                        ActiveStreams[stream.Id] = stream;

                        stream.OnClose += (o, args) =>
                        {
                            if (!ActiveStreams.Remove(ActiveStreams[args.Id]))
                            {
                                throw new ArgumentException("Cant remove stream from ActiveStreams");
                            }
                        };

                        _toBeContinuedFrame = null;
                        _toBeContinuedHeaders = null;
                        break;

                    case FrameType.Priority:
                        var priorityFrame = (PriorityFrame) frame;
                        stream = GetStream(priorityFrame.StreamId);
                        if (_usePriorities)
                        {
                            stream.Priority = priorityFrame.Priority;
                        }
                        break;

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

                        Console.WriteLine("Got rst with code {0}", resetFrame.StatusCode);
                        stream.Dispose();
                        break;
                    case FrameType.Data:
                        var dataFrame = (DataFrame) frame;
                        stream = GetStream(dataFrame.StreamId);

                        //Aggressive window update
                        if (stream.IsFlowControlEnabled)
                        {
                            stream.WriteWindowUpdate(2000000);
                        }
                        break;
                    case FrameType.Ping:
                        var pingFrame = (PingFrame) frame;
                        if (pingFrame.IsPong)
                        {
                              _wasPingReceived = true;
                              _pingReceived.Set();
                        }
                        else
                        {
                            var pingResonseFrame = new PingFrame(true);
                            _writeQueue.WriteFrame(pingResonseFrame);
                        }
                        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;
                        }

                        _wasSettingsReceived = true;
                        _settingsManager.ProcessSettings((SettingsFrame) frame, this, _flowControlManager);
                        break;
                    case FrameType.WindowUpdate:
                        if (_useFlowControl)
                        {
                            var windowFrame = (WindowUpdateFrame) frame;

                            stream = GetStream(windowFrame.StreamId);

                            stream.UpdateWindowSize(windowFrame.Delta);
                            stream.PumpUnshippedFrames();
                        }
                        break;

                    case FrameType.GoAway:
                        _goAwayReceived = true;
                        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
                    Console.WriteLine("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?
                }
            }
        }
 public RequestSentEventArgs(Http2Stream stream)
 {
     Stream = stream;
 }
 public RequestSentEventArgs(Http2Stream stream)
 {
     Stream = stream;
 }