Beispiel #1
0
        /// <summary>
        /// Http2Client
        /// </summary>
        /// <param name="host"></param>
        /// <param name="ip"></param>
        /// <param name="scheme"></param>
        /// <param name="path"></param>
        /// <param name="authority"></param>
        /// <param name="useHttp1Upgrade"></param>
        /// <param name="size"></param>
        /// <param name="timeOut"></param>
        public Http2Client(string host, int ip, string scheme, string path, string authority, bool useHttp1Upgrade = false, int size = 1024, int timeOut = 180 * 1000)
        {
            _path = path;

            _authority = authority;

            var options = SocketOptionBuilder.Instance.SetSocket(Sockets.Model.SAEASocketType.Tcp)
                          .UseStream()
                          .SetIP(host)
                          .SetPort(ip)
                          .SetReadBufferSize(size)
                          .SetWriteBufferSize(size)
                          .SetTimeOut(timeOut)
                          .Build();

            _clientSocket = SocketFactory.CreateClientSocket(options);

            _clientSocket.ConnectAsync().GetAwaiter();

            var config = new ConnectionConfigurationBuilder(false)
                         .UseSettings(Settings.Default)
                         .UseHuffmanStrategy(HuffmanStrategy.IfSmaller)
                         .Build();

            var wrappedStreams = _clientSocket.Socket.CreateStreams();

            if (useHttp1Upgrade)
            {
                var upgrade = new ClientUpgradeRequestBuilder()
                              .SetHttp2Settings(config.Settings)
                              .Build();

                var upgradeReadStream = new UpgradeReadStream(wrappedStreams.ReadableStream);

                var needExplicitStreamClose = true;

                try
                {
                    var upgradeHeader = "OPTIONS / HTTP/1.1\r\n" +
                                        "Host: " + host + "\r\n" +
                                        "Connection: Upgrade, HTTP2-Settings\r\n" +
                                        "Upgrade: h2c\r\n" +
                                        "HTTP2-Settings: " + upgrade.Base64EncodedSettings + "\r\n\r\n";

                    var encodedHeader = Encoding.ASCII.GetBytes(upgradeHeader);

                    wrappedStreams.WriteableStream.WriteAsync(new ArraySegment <byte>(encodedHeader)).GetAwaiter();

                    upgradeReadStream.WaitForHttpHeader().GetAwaiter();

                    var headerBytes = upgradeReadStream.HeaderBytes;

                    upgradeReadStream.ConsumeHttpHeader();

                    var response = Http1Response.ParseFrom(Encoding.ASCII.GetString(headerBytes.Array, headerBytes.Offset, headerBytes.Count - 4));

                    if (response.StatusCode != "101")
                    {
                        throw new Exception("升级失败");
                    }

                    if (!response.Headers.Any(hf => hf.Key == "connection" && hf.Value == "Upgrade") ||
                        !response.Headers.Any(hf => hf.Key == "upgrade" && hf.Value == "h2c"))
                    {
                        throw new Exception("升级失败");
                    }

                    needExplicitStreamClose = false;

                    var conn = new Connection(config, upgradeReadStream, wrappedStreams.WriteableStream, options: new Connection.Options
                    {
                        ClientUpgradeRequest = upgrade,
                    });


                    var upgradeStream = upgrade.UpgradeRequestStream.GetAwaiter().GetResult();

                    upgradeStream.Cancel();

                    _conn = conn;
                }
                finally
                {
                    if (needExplicitStreamClose)
                    {
                        wrappedStreams.WriteableStream.CloseAsync();
                    }
                }
            }
            else
            {
                _conn = new Connection(config, wrappedStreams.ReadableStream, wrappedStreams.WriteableStream, options: new Connection.Options());
            }
        }
Beispiel #2
0
    async Task <Connection> CreateUpgradeConnection(string host, int port)
    {
        // HTTP/2 settings
        var config =
            new ConnectionConfigurationBuilder(false)
            .UseSettings(Settings.Default)
            .UseHuffmanStrategy(HuffmanStrategy.IfSmaller)
            .Build();

        // Prepare connection upgrade
        var upgrade =
            new ClientUpgradeRequestBuilder()
            .SetHttp2Settings(config.Settings)
            .Build();

        // Create a TCP connection
        logger.LogInformation($"Starting to connect to {host}:{port}");
        var tcpClient = new TcpClient();
        await tcpClient.ConnectAsync(host, port);

        tcpClient.Client.NoDelay = true;
        logger.LogInformation("Connected to remote");

        // Create HTTP/2 stream abstraction on top of the socket
        var wrappedStreams    = tcpClient.Client.CreateStreams();
        var upgradeReadStream = new UpgradeReadStream(wrappedStreams.ReadableStream);

        var needExplicitStreamClose = true;

        try
        {
            // Send a HTTP/1.1 upgrade request with the necessary fields
            var upgradeHeader =
                "OPTIONS / HTTP/1.1\r\n" +
                "Host: " + host + "\r\n" +
                "Connection: Upgrade, HTTP2-Settings\r\n" +
                "Upgrade: h2c\r\n" +
                "HTTP2-Settings: " + upgrade.Base64EncodedSettings + "\r\n\r\n";
            logger.LogInformation("Sending upgrade request:\n" + upgradeHeader);
            var encodedHeader = Encoding.ASCII.GetBytes(upgradeHeader);
            await wrappedStreams.WriteableStream.WriteAsync(
                new ArraySegment <byte>(encodedHeader));

            // Wait for the upgrade response
            await upgradeReadStream.WaitForHttpHeader();

            var headerBytes = upgradeReadStream.HeaderBytes;

            logger.LogInformation(
                "Received HTTP/1.1 response: " +
                Encoding.ASCII.GetString(headerBytes.Array, 0, headerBytes.Count));

            // Try to parse the upgrade response as HTTP/1 status and check whether
            // the upgrade was successful.
            var response = Http1Response.ParseFrom(
                Encoding.ASCII.GetString(
                    headerBytes.Array, headerBytes.Offset, headerBytes.Count - 4));
            // Mark the HTTP/1.1 bytes as read
            upgradeReadStream.ConsumeHttpHeader();

            if (response.StatusCode != "101")
            {
                throw new Exception("Upgrade failed");
            }
            if (!response.Headers.Any(hf => hf.Key == "connection" && hf.Value == "Upgrade") ||
                !response.Headers.Any(hf => hf.Key == "upgrade" && hf.Value == "h2c"))
            {
                throw new Exception("Upgrade failed");
            }

            logger.LogInformation("Connection upgrade succesful!");

            // If we get here then the connection will be reponsible for closing
            // the stream
            needExplicitStreamClose = false;

            // Build a HTTP connection on top of the stream abstraction
            var connLogger = verbose ? logProvider.CreateLogger("HTTP2Conn") : null;
            var conn       = new Connection(
                config, upgradeReadStream, wrappedStreams.WriteableStream,
                options: new Connection.Options
            {
                Logger = connLogger,
                ClientUpgradeRequest = upgrade,
            });

            // Retrieve the response stream for the connection upgrade.
            var upgradeStream = await upgrade.UpgradeRequestStream;
            // As we made the upgrade via a dummy OPTIONS request we are not
            // really interested in the result of the upgrade request
            upgradeStream.Cancel();

            return(conn);
        }
        finally
        {
            if (needExplicitStreamClose)
            {
                await wrappedStreams.WriteableStream.CloseAsync();
            }
        }
    }
Beispiel #3
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);
        });
    }