Beispiel #1
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 #2
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());
            }
        }