public static async Task WriteContinuation(
            this IWriteAndCloseableByteStream stream,
            Encoder encoder,
            uint streamId,
            IEnumerable <HeaderField> headers,
            bool endOfHeaders = true)
        {
            var outBuf = new byte[Settings.Default.MaxFrameSize];
            var result = encoder.EncodeInto(new ArraySegment <byte>(outBuf), headers);

            // Check if all headers could be encoded
            if (result.FieldCount != headers.Count())
            {
                throw new Exception("Could not encode all headers");
            }

            byte flags = 0;

            if (endOfHeaders)
            {
                flags |= (byte)ContinuationFrameFlags.EndOfHeaders;
            }
            var fh = new FrameHeader
            {
                Type     = FrameType.Continuation,
                Length   = result.UsedBytes,
                Flags    = flags,
                StreamId = streamId,
            };
            await stream.WriteFrameHeader(fh);

            await stream.WriteAsync(
                new ArraySegment <byte>(outBuf, 0, result.UsedBytes));
        }
        public static async Task WriteFrameHeader(
            this IWriteAndCloseableByteStream stream,
            FrameHeader fh)
        {
            var headerBytes = new byte[FrameHeader.HeaderSize];

            fh.EncodeInto(new ArraySegment <byte>(headerBytes));
            await stream.WriteAsync(new ArraySegment <byte>(headerBytes));
        }
        public static async Task WritePing(
            this IWriteAndCloseableByteStream stream, byte[] data, bool isAck)
        {
            var pingHeader = new FrameHeader
            {
                Type     = FrameType.Ping,
                Flags    = isAck ? (byte)PingFrameFlags.Ack : (byte)0,
                Length   = 8,
                StreamId = 0,
            };
            await stream.WriteFrameHeader(pingHeader);

            await stream.WriteAsync(new ArraySegment <byte>(data, 0, 8));
        }
        public static async Task WriteWithTimeout(
            this IWriteAndCloseableByteStream stream, ArraySegment <byte> buf)
        {
            var writeTask   = stream.WriteAsync(buf);
            var timeoutTask = Task.Delay(WriteTimeout);
            var combined    = Task.WhenAny(new Task[] { writeTask, timeoutTask });
            var done        = await combined;

            if (done == writeTask)
            {
                await writeTask;
                return;
            }
            throw new TimeoutException();
        }
        public static async Task WriteSettings(
            this IWriteAndCloseableByteStream stream, Settings settings)
        {
            var settingsData = new byte[settings.RequiredSize];
            var fh           = new FrameHeader
            {
                Type     = FrameType.Settings,
                Length   = settingsData.Length,
                Flags    = 0,
                StreamId = 0,
            };

            settings.EncodeInto(new ArraySegment <byte>(settingsData));
            await stream.WriteFrameHeader(fh);

            await stream.WriteAsync(new ArraySegment <byte>(settingsData));
        }
        public static async Task WritePriority(
            this IWriteAndCloseableByteStream stream,
            uint streamId, PriorityData prioData)
        {
            var fh = new FrameHeader
            {
                Type     = FrameType.Priority,
                Flags    = 0,
                Length   = PriorityData.Size,
                StreamId = streamId,
            };
            await stream.WriteFrameHeader(fh);

            var payload = new byte[PriorityData.Size];

            prioData.EncodeInto(new ArraySegment <byte>(payload));
            await stream.WriteAsync(new ArraySegment <byte>(payload));
        }
        public static async Task WriteResetStream(
            this IWriteAndCloseableByteStream stream, uint streamId, ErrorCode errc)
        {
            var fh = new FrameHeader
            {
                Type     = FrameType.ResetStream,
                Flags    = 0,
                Length   = ResetFrameData.Size,
                StreamId = streamId,
            };
            var data = new ResetFrameData
            {
                ErrorCode = errc,
            };
            var dataBytes = new byte[ResetFrameData.Size];

            data.EncodeInto(new ArraySegment <byte>(dataBytes));
            await stream.WriteFrameHeader(fh);

            await stream.WriteAsync(new ArraySegment <byte>(dataBytes));
        }
        public static async Task WriteWindowUpdate(
            this IWriteAndCloseableByteStream stream, uint streamId, int amount)
        {
            var windowUpdateHeader = new FrameHeader
            {
                Type     = FrameType.WindowUpdate,
                Flags    = 0,
                Length   = WindowUpdateData.Size,
                StreamId = streamId,
            };
            var data = new WindowUpdateData
            {
                WindowSizeIncrement = amount,
            };
            var dataBytes = new byte[WindowUpdateData.Size];

            data.EncodeInto(new ArraySegment <byte>(dataBytes));
            await stream.WriteFrameHeader(windowUpdateHeader);

            await stream.WriteAsync(new ArraySegment <byte>(dataBytes));
        }
        public static async Task WriteGoAway(
            this IWriteAndCloseableByteStream stream,
            uint lastStreamId,
            ErrorCode errc,
            byte[] debugData = null)
        {
            if (debugData == null)
            {
                debugData = new byte[0];
            }

            var goAwayData = new GoAwayFrameData
            {
                Reason = new GoAwayReason
                {
                    LastStreamId = lastStreamId,
                    ErrorCode    = errc,
                    DebugData    = new ArraySegment <byte>(debugData),
                },
            };

            var fh = new FrameHeader
            {
                Type     = FrameType.GoAway,
                Flags    = 0,
                StreamId = 0,
                Length   = goAwayData.RequiredSize,
            };

            var dataBytes = new byte[goAwayData.RequiredSize];

            goAwayData.EncodeInto(new ArraySegment <byte>(dataBytes));
            await stream.WriteFrameHeader(fh);

            await stream.WriteAsync(new ArraySegment <byte>(dataBytes));
        }
예제 #10
0
    static async Task HandleConnection(
        ILoggerProvider logProvider,
        ConnectionConfiguration config,
        bool useUpgrade,
        IReadableByteStream inputStream,
        IWriteAndCloseableByteStream outputStream,
        int connectionId)
    {
        var upgradeReadStream        = new UpgradeReadStream(inputStream);
        ServerUpgradeRequest upgrade = null;

        try
        {
            // Wait for either HTTP/1 upgrade header or HTTP/2 magic header
            await upgradeReadStream.WaitForHttpHeader();

            var headerBytes = upgradeReadStream.HeaderBytes;
            if (MaybeHttpStart(headerBytes))
            {
                // This seems to be a HTTP/2 request
                // No upgrade necessary
                // Make the header rereadable by the stream reader consumer,
                // so that the library can also read the preface
                upgradeReadStream.UnreadHttpHeader();
            }
            else
            {
                // This seems to be a HTTP/1 request
                // Parse the header and check whether it's an upgrade
                var request = Http1Request.ParseFrom(
                    Encoding.ASCII.GetString(
                        headerBytes.Array, headerBytes.Offset, headerBytes.Count - 4));
                // Assure that the HTTP/2 library does not get passed the HTTP/1 request
                upgradeReadStream.ConsumeHttpHeader();

                if (request.Protocol != "HTTP/1.1")
                {
                    throw new Exception("Invalid upgrade request");
                }

                // If the request has some payload we can't process it in this demo
                string contentLength;
                if (request.Headers.TryGetValue("content-length", out contentLength))
                {
                    await outputStream.WriteAsync(
                        new ArraySegment <byte>(UpgradeErrorResponse));

                    await outputStream.CloseAsync();

                    return;
                }

                string connectionHeader;
                string hostHeader;
                string upgradeHeader;
                string http2SettingsHeader;
                if (!request.Headers.TryGetValue("connection", out connectionHeader) ||
                    !request.Headers.TryGetValue("host", out hostHeader) ||
                    !request.Headers.TryGetValue("upgrade", out upgradeHeader) ||
                    !request.Headers.TryGetValue("http2-settings", out http2SettingsHeader) ||
                    upgradeHeader != "h2c" ||
                    http2SettingsHeader.Length == 0)
                {
                    throw new Exception("Invalid upgrade request");
                }

                var connParts =
                    connectionHeader
                    .Split(new char[] { ',' })
                    .Select(p => p.Trim())
                    .ToArray();
                if (connParts.Length != 2 ||
                    !connParts.Contains("Upgrade") ||
                    !connParts.Contains("HTTP2-Settings"))
                {
                    throw new Exception("Invalid upgrade request");
                }

                var headers = new List <HeaderField>();
                headers.Add(new HeaderField {
                    Name = ":method", Value = request.Method
                });
                headers.Add(new HeaderField {
                    Name = ":path", Value = request.Path
                });
                headers.Add(new HeaderField {
                    Name = ":scheme", Value = "http"
                });
                foreach (var kvp in request.Headers)
                {
                    // Skip Connection upgrade related headers
                    if (kvp.Key == "connection" ||
                        kvp.Key == "upgrade" ||
                        kvp.Key == "http2-settings")
                    {
                        continue;
                    }
                    headers.Add(new HeaderField
                    {
                        Name  = kvp.Key,
                        Value = kvp.Value,
                    });
                }

                var upgradeBuilder = new ServerUpgradeRequestBuilder();
                upgradeBuilder.SetHeaders(headers);
                upgradeBuilder.SetHttp2Settings(http2SettingsHeader);
                upgrade = upgradeBuilder.Build();

                if (!upgrade.IsValid)
                {
                    await outputStream.WriteAsync(
                        new ArraySegment <byte>(UpgradeErrorResponse));

                    await outputStream.CloseAsync();

                    return;
                }

                // Respond to upgrade
                await outputStream.WriteAsync(
                    new ArraySegment <byte>(UpgradeSuccessResponse));
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("Error during connection upgrade: {0}", e.Message);
            await outputStream.CloseAsync();

            return;
        }

        // Build a HTTP connection on top of the stream abstraction
        var http2Con = new Connection(
            config, upgradeReadStream, outputStream,
            options: new Connection.Options
        {
            Logger = logProvider.CreateLogger("HTTP2Conn" + connectionId),
            ServerUpgradeRequest = upgrade,
        });

        // Close the connection if we get a GoAway from the client
        var remoteGoAwayTask      = http2Con.RemoteGoAwayReason;
        var closeWhenRemoteGoAway = Task.Run(async() =>
        {
            await remoteGoAwayTask;
            await http2Con.GoAwayAsync(ErrorCode.NoError, true);
        });
    }