예제 #1
0
        public async Task<Http2Response> Send (CancellationToken cancelToken, Uri uri, HttpMethod method, NameValueCollection headers = null, Stream data = null)
        {
            var semaphoreClose = new SemaphoreSlim(0);

            await connection.Connect ();

            var stream = await streamManager.Get ();
            stream.OnFrameReceived += async (frame) =>
            {
                // Check for an end of stream state
                if (stream.State == StreamState.HalfClosedRemote || stream.State == StreamState.Closed)
                    semaphoreClose.Release ();
            };

            var allHeaders = new NameValueCollection ();
            allHeaders.Add (":method", method.Method.ToUpperInvariant ());
            allHeaders.Add (":path", uri.PathAndQuery);
            allHeaders.Add (":scheme", uri.Scheme);
            allHeaders.Add (":authority", uri.Authority);
            if (headers != null && headers.Count > 0)
                allHeaders.Add (headers);

            var headerData = Util.PackHeaders (allHeaders, connection.Settings.HeaderTableSize);

            var numFrames = Math.Ceiling ((double)headerData.Length / (double)connection.Settings.MaxFrameSize);

            for (int i = 0; i < numFrames; i++) {
                // First item is headers frame, others are continuation
                IFrameContainsHeaders frame = (i == 0) ? 
                    (IFrameContainsHeaders)new HeadersFrame (stream.StreamIdentifer) 
                    : (IFrameContainsHeaders)new ContinuationFrame (stream.StreamIdentifer);

                // Set end flag if this is the last item
                if (i == numFrames - 1)
                    frame.EndHeaders = true;

                var maxFrameSize = connection.Settings.MaxFrameSize;

                var amt = maxFrameSize;
                if ( i * maxFrameSize + amt > headerData.Length)
                    amt = (uint)headerData.Length - (uint)(i * maxFrameSize);
                frame.HeaderBlockFragment = new byte[amt];
                Array.Copy (headerData, i * maxFrameSize, frame.HeaderBlockFragment, 0, amt);

                await connection.QueueFrame (frame);
            }
            // TODO: Need to send multiple frames if the frame content length is too big
            // This will require checking what the actual 

            if (data != null) {
                var sentEndOfStream = false;
                var supportsPosLength = true; // Keep track of if we threw exceptions trying pos/len of stream

                // Break stream up into data frames within allowed size
                var dataFrameBuffer = new byte[connection.Settings.MaxFrameSize];
                while (true) {

                    var rd = await data.ReadAsync (dataFrameBuffer, 0, dataFrameBuffer.Length);

                    if (rd <= 0)
                        break;
                    
                    var dataFrame = new DataFrame (stream.StreamIdentifer);
                    dataFrameBuffer.CopyTo (dataFrame.Data, 0);

                    try {
                        // See if the stream supports Length / Position to try and detect EOS
                        // we also want to see if we previously had an exception trying this
                        // and not try again if we did, since throwing exceptions every single
                        // read operation is wasteful
                        if (supportsPosLength && data.Position >= data.Length) {
                            dataFrame.EndStream = true;
                            sentEndOfStream = true;
                        }
                    } catch {
                        supportsPosLength = false;
                        sentEndOfStream = false;
                    }

                    await connection.QueueFrame (dataFrame);
                }

                // Send an empty frame with end of stream flag
                if (!sentEndOfStream)
                    await connection.QueueFrame (new DataFrame (stream.StreamIdentifer) { EndStream = true });

            }

            if (!await semaphoreClose.WaitAsync (ConnectionSettings.ConnectionTimeout, cancelToken))
                throw new TimeoutException ();

            var responseData = new List<byte> ();
            var rxHeaderData = new List<byte> ();

            foreach (var f in stream.ReceivedFrames) {
                if (f.Type == FrameType.Headers || f.Type == FrameType.Continuation) {
                    // Get the header data and add it to our buffer
                    var fch = (IFrameContainsHeaders)f;
                    if (fch.HeaderBlockFragment != null && fch.HeaderBlockFragment.Length > 0)
                        rxHeaderData.AddRange (fch.HeaderBlockFragment);    
                } else if (f.Type == FrameType.PushPromise) {
                    // TODO: In the future we need to implement PushPromise beyond grabbing header data
                    var fch = (IFrameContainsHeaders)f;
                    if (fch.HeaderBlockFragment != null && fch.HeaderBlockFragment.Length > 0)
                        rxHeaderData.AddRange (fch.HeaderBlockFragment);    
                } else if (f.Type == FrameType.Data) {
                    responseData.AddRange ((f as DataFrame).Data);
                } else if (f.Type == FrameType.GoAway) {
                    var fga = f as GoAwayFrame;
                    if (fga != null && fga.AdditionalDebugData != null && fga.AdditionalDebugData.Length > 0)
                        responseData.AddRange (fga.AdditionalDebugData);
                }
            }

            var responseHeaders = Util.UnpackHeaders (rxHeaderData.ToArray (), 
                connection.Settings.MaxHeaderListSize.HasValue ? (int)connection.Settings.MaxHeaderListSize.Value : 8192, 
                (int)connection.Settings.HeaderTableSize);

            var strStatus = "500";
            if (responseHeaders [":status"] != null)
                strStatus = responseHeaders [":status"];
            
            var statusCode = HttpStatusCode.OK;
            Enum.TryParse<HttpStatusCode> (strStatus, out statusCode);

            // Remove the stream from being tracked since we're done with it
            await streamManager.Cleanup (stream.StreamIdentifer);

            return new Http2Response {
                Status = statusCode,
                Stream = stream,
                Headers = responseHeaders,
                Body = responseData.ToArray ()
            };
        }
예제 #2
0
        public async Task<Http2Response> Send (Uri uri, HttpMethod method, NameValueCollection headers = null, byte[] data = null)
        {
            var semaphoreClose = new SemaphoreSlim(0);

            await connection.Connect ();

            var stream = await connection.CreateStream ();
            stream.OnFrameReceived += (frame) =>
            {
                if (stream.State == StreamState.Closed)
                    semaphoreClose.Release();
            };

            //await connection.SendFrame(new SettingsFrame());

            var headersFrame = new HeadersFrame (stream.StreamIdentifer);
            headersFrame.Headers.Add (":method", method.Method.ToUpperInvariant ());
            headersFrame.Headers.Add (":path", uri.PathAndQuery);
            headersFrame.Headers.Add (":scheme", uri.Scheme);
            headersFrame.Headers.Add (":authority", uri.Authority);
            if (headers != null && headers.Count > 0)
                headersFrame.Headers.Add (headers);            
            headersFrame.EndHeaders = true;

            if (data != null && data.Length > 0) {

                await connection.SendFrame (headersFrame);

                var dataFrame = new DataFrame (stream.StreamIdentifer);
                dataFrame.Data = data;
                dataFrame.EndStream = true;
                

                await connection.SendFrame (dataFrame);

            } else {                
                headersFrame.EndStream = true;

                await connection.SendFrame (headersFrame);
            }

            if (!await semaphoreClose.WaitAsync (connection.ConnectionTimeout))
                throw new TimeoutException ();
            
            var responseData = new List<byte> ();
            var responseHeaders = new NameValueCollection ();

            foreach (var f in stream.Frames) {
                if (f.Type == FrameType.Headers) {                    
                    responseHeaders = (f as HeadersFrame)?.Headers ?? new NameValueCollection ();
                } else if (f.Type == FrameType.Data) {
                    responseData.AddRange ((f as DataFrame).Data);
                } else if (f.Type == FrameType.Continuation) {
                    var h = (f as ContinuationFrame).Headers ?? new NameValueCollection ();
                    responseHeaders.Add (h);
                } else if (f.Type == FrameType.GoAway) {
                    var fga = f as GoAwayFrame;
                    if (fga != null && fga.AdditionalDebugData != null && fga.AdditionalDebugData.Length > 0)
                        responseData.AddRange (fga.AdditionalDebugData);
                }
            }

            var strStatus = "500";
            if (responseHeaders [":status"] != null)
                strStatus = responseHeaders [":status"];
            
            var statusCode = HttpStatusCode.OK;
            Enum.TryParse<HttpStatusCode> (strStatus, out statusCode);
                

            return new Http2Response {
                Status = statusCode,
                Stream = stream,
                Headers = responseHeaders,
                Body = responseData.ToArray ()
            };
        }
예제 #3
0
        async void read ()
        {                        
            int rx = 0;
            byte[] b = new byte[4096];

            while (true) {

                try {
                    rx = await clientStream.ReadAsync(b, 0, b.Length);
                } catch {
                    rx = -1;
                }

                if (rx > 0) {

                    Console.WriteLine ("RX: {0} bytes", rx);

                    for (int i = 0; i < rx; i++)
                        buffer.Add (b [i]);
                    
                    while (true) 
                    {                                                
                        // We need at least 9 bytes to process the frame 
                        // 9 octets is the frame header length
                        if (buffer.Count < 9)
                            break;
                        
                        // Find out the frame length
                        // which is a 24 bit uint, so we need to convert this as c# uint is 32 bit
                        var flen = new byte[4];
                        flen [0] = 0x0;
                        flen [1] = buffer.ElementAt (0);
                        flen [2] = buffer.ElementAt (1);
                        flen [3] = buffer.ElementAt (2);

                        var frameLength = BitConverter.ToUInt32 (flen.EnsureBigEndian (), 0);

                        // If we are expecting a payload that's bigger than what's in our buffer
                        // we should keep reading from the stream 
                        if (buffer.Count - 9 < frameLength)
                            break;

                        // If we made it this far, the buffer has all the data we need, let's get it out to process
                        var data = buffer.GetRange (0, (int)frameLength + 9).ToArray ();
                        // remove the processed info from the buffer
                        buffer.RemoveRange (0, (int)frameLength + 9);

                        // Get the Frame Type so we can instantiate the right subclass
                        var frameType = data [3]; // 4th byte in frame header is TYPE

                        // Don't need the flags yet
                        //var frameFlags = data [4]; // 5th byte is FLAGS

                        // we need to turn the stream id into a uint
                        var frameStreamIdData = new byte[4]; 
                        Array.Copy (data, 5, frameStreamIdData, 0, 4);
                        uint frameStreamId = Util.ConvertFromUInt31 (frameStreamIdData.EnsureBigEndian ());

                        Frame frame = null;

                        var ft = (FrameType)frameType;

                        switch (ft) {
                        case FrameType.Data:
                            frame = new DataFrame ();
                            break;
                        case FrameType.Headers:
                            frame = new HeadersFrame ();
                            break;
                        case FrameType.Priority:
                            frame = new PriorityFrame ();
                            break;
                        case FrameType.RstStream:
                            frame = new RstStreamFrame ();
                            break;
                        case FrameType.Settings:
                            frame = new SettingsFrame ();
                            break;
                        case FrameType.PushPromise:
                            frame = new PushPromiseFrame ();
                            break;
                        case FrameType.Ping:
                            frame = new PingFrame ();
                            break;
                        case FrameType.GoAway:
                            frame = new GoAwayFrame ();
                            break;
                        case FrameType.WindowUpdate:
                            frame = new WindowUpdateFrame ();
                            break;
                        case FrameType.Continuation:
                            frame = new ContinuationFrame ();
                            break;
                        }

                        try {
                            // Call the specific subclass implementation to parse
                            if (frame != null)
                                frame.Parse (data);
                        } catch (Exception ex) {
                            Console.WriteLine ("Parsing Frame Failed: " + ex);
                            throw ex;
                        }

                        // If we are waiting on the connection preface from server
                        // and it's the right frame type, set our resetevent
                        if (ft == FrameType.Settings 
                            && resetEventConnectionSettingsFrame != null 
                            && !resetEventConnectionSettingsFrame.IsSet) {

                            var settingsFrame = frame as SettingsFrame;
                            // ack the settings from the server
                            settingsFrame.Ack = true;
                            await SendFrame (settingsFrame);
                            resetEventConnectionSettingsFrame.Set ();
                        }

                        try {
                            if (frameStreamId == 0) {
                                foreach (var s in Streams) {
                                    if (s.Value.State != StreamState.Closed)
                                        s.Value.ProcessFrame(frame);
                                }
                            } else {
                                var stream = await GetStream(frameStreamId);
                                stream.ProcessFrame(frame);
                            }
                        } catch (Exception ex) {
                            Console.WriteLine ("Error Processing Frame: " + ex);
                            throw ex;
                        }
                    }

                } else {
                    // TODO: Connection error
                    //throw new Exception ("Connection Error");
                    break;
                }


            }

            Disconnect();
        }