Exemple #1
0
        public void CreatingAConnectionWithInvalidUpgradeShouldThrow()
        {
            var inPipe  = new BufferedPipe(1024);
            var outPipe = new BufferedPipe(1024);

            var config = new ConnectionConfigurationBuilder(true)
                         .UseStreamListener(s => false)
                         .Build();

            var builder = new ServerUpgradeRequestBuilder();

            builder.SetHeaders(DefaultGetHeaders.ToList());
            builder.SetHttp2Settings("!");
            var upgrade = builder.Build();

            Assert.False(upgrade.IsValid);

            var ex = Assert.Throws <ArgumentException>(() =>
            {
                var conn = new Connection(
                    config, inPipe, outPipe,
                    new Connection.Options
                {
                    Logger = loggerProvider.CreateLogger(""),
                    ServerUpgradeRequest = upgrade,
                });
            });

            Assert.Equal(
                "The ServerUpgradeRequest is invalid.\n" +
                "Invalid upgrade requests must be denied by the HTTP/1 handler",
                ex.Message);
        }
Exemple #2
0
    async Task <Connection> CreateDirectConnection(string host, int port)
    {
        // HTTP/2 settings
        var config =
            new ConnectionConfigurationBuilder(false)
            .UseSettings(Settings.Default)
            .UseHuffmanStrategy(HuffmanStrategy.IfSmaller)
            .Build();

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

        logger.LogInformation("Connected to remote");
        tcpClient.Client.NoDelay = true;
        // Create HTTP/2 stream abstraction on top of the socket
        var wrappedStreams = tcpClient.Client.CreateStreams();

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

        return(conn);
    }
 public void Parse(Dictionary<string, string> parameters, ConnectionConfigurationBuilder builder)
 {
     if (requiredParameters.All(parameters.ContainsKey))
     {
         configAction(parameters, builder);
     }
 }
Exemple #4
0
    static async Task AcceptTask(TcpListener listener, ILoggerProvider logProvider)
    {
        var connectionId = 0;

        var config =
            new ConnectionConfigurationBuilder(isServer: true)
            .UseStreamListener(AcceptIncomingStream)
            .UseHuffmanStrategy(HuffmanStrategy.Never)
            .UseBufferPool(Buffers.Pool)
            .Build();

        while (true)
        {
            // Accept TCP sockets
            var clientSocket = await listener.AcceptSocketAsync();

            clientSocket.NoDelay = true;
            // Create HTTP/2 stream abstraction on top of the socket
            var wrappedStreams = clientSocket.CreateStreams();
            // Alternatively on top of a System.IO.Stream
            //var netStream = new NetworkStream(clientSocket, true);
            //var wrappedStreams = netStream.CreateStreams();

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

            connectionId++;
        }
    }
        private Connection BuildConnection(
            bool isServer,
            IReadableByteStream inputStream,
            IWriteAndCloseableByteStream outputStream)
        {
            ILogger logger = null;

            if (loggerProvider != null)
            {
                logger = loggerProvider.CreateLogger("http2Con");
            }

            // Decrease the timeout for the preface,
            // as this speeds the test up
            var config =
                new ConnectionConfigurationBuilder(isServer)
                .UseStreamListener((s) => false)
                .UseClientPrefaceTimeout(200)
                .Build();

            return(new Connection(
                       config, inputStream, outputStream,
                       new Connection.Options
            {
                Logger = logger,
            }));
        }
 private static ConnectionSettings ParseConnectionSettings(string connectionString)
 {
     var parser = new ConnectionStringParser();
     var builder = new ConnectionConfigurationBuilder();
     parser.Parse(connectionString, builder);
     return builder.ConnectionSettings;
 }
Exemple #7
0
        private static ConnectionSettings ParseConnectionSettings(string connectionString)
        {
            var parser  = new ConnectionStringParser();
            var builder = new ConnectionConfigurationBuilder();

            parser.Parse(connectionString, builder);
            return(builder.ConnectionSettings);
        }
Exemple #8
0
        public async Task ClientUpgradeRequestShouldYieldStream1()
        {
            var inPipe  = new BufferedPipe(1024);
            var outPipe = new BufferedPipe(1024);

            var upgrade = new ClientUpgradeRequestBuilder().Build();
            var config  = new ConnectionConfigurationBuilder(false)
                          .Build();

            var conn = new Connection(
                config, inPipe, outPipe,
                new Connection.Options
            {
                Logger = loggerProvider.CreateLogger("http2Con"),
                ClientUpgradeRequest = upgrade,
            });

            await conn.PerformHandshakes(inPipe, outPipe);

            var stream = await upgrade.UpgradeRequestStream;

            Assert.Equal(1u, stream.Id);
            Assert.Equal(1, conn.ActiveStreamCount);
            Assert.Equal(StreamState.HalfClosedLocal, stream.State);

            var readHeadersTask = stream.ReadHeadersAsync();

            Assert.False(readHeadersTask.IsCompleted);

            var hEncoder = new Http2.Hpack.Encoder();
            await inPipe.WriteHeaders(hEncoder, 1u, false, DefaultStatusHeaders);

            Assert.True(
                await Task.WhenAny(
                    readHeadersTask,
                    Task.Delay(ReadableStreamTestExtensions.ReadTimeout))
                == readHeadersTask,
                "Expected to read headers, got timeout");
            var headers = await readHeadersTask;

            Assert.True(headers.SequenceEqual(DefaultStatusHeaders));
            Assert.Equal(StreamState.HalfClosedLocal, stream.State);

            await inPipe.WriteData(1u, 100, 5, true);

            var data = await stream.ReadAllToArrayWithTimeout();

            Assert.True(data.Length == 100);
            Assert.Equal(StreamState.Closed, stream.State);
            Assert.Equal(0, conn.ActiveStreamCount);
        }
        public void Parse(string connectionString, ConnectionConfigurationBuilder builder)
        {
            var settingsDictionary =
                connectionString.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries)
                                .Select(x => x.Trim(new[] {' '}))
                                .Select(x => x.Split(new[] {'='}, StringSplitOptions.RemoveEmptyEntries))
                                .Where(x => x.Length == 1 || x.Length == 2)
                                .ToDictionary(x => x[0], x => x.Length == 1 ? null : x[1]);

            foreach (var parser in propertyParsers)
            {
                parser.Parse(settingsDictionary, builder);
            }
        }
Exemple #10
0
    static async Task AcceptTask(TcpListener listener, ILoggerProvider logProvider)
    {
        var connectionId = 0;

        var settings = Settings.Default;

        settings.MaxConcurrentStreams = 50;

        var config =
            new ConnectionConfigurationBuilder(true)
            .UseStreamListener(AcceptIncomingStream)
            .UseSettings(settings)
            .UseHuffmanStrategy(HuffmanStrategy.IfSmaller)
            .Build();

        while (true)
        {
            // Accept TCP sockets
            var clientSocket = await listener.AcceptSocketAsync();

            clientSocket.NoDelay = true;
            // Create an SSL stream
            var sslStream = new SslStream(new NetworkStream(clientSocket, true));
            // Authenticate on the stream
            await sslStream.AuthenticateAsServerAsync(options, CancellationToken.None);

            // wrap the SslStream
            var wrappedStreams = sslStream.CreateStreams();
            // Build a HTTP connection on top of the stream abstraction
            var http2Con = new Connection(
                config, wrappedStreams.ReadableStream, wrappedStreams.WriteableStream,
                options: new Connection.Options
            {
                Logger = logProvider.CreateLogger("HTTP2Conn" + connectionId),
            });

            // 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);
            });

            connectionId++;
        }
    }
Exemple #11
0
        private async Task <Connection> CreateDirectConnection(Uri uri)
        {
            var config =
                new ConnectionConfigurationBuilder(false)
                .UseSettings(Settings.Default)
                .UseHuffmanStrategy(HuffmanStrategy.IfSmaller)
                .Build();

            var tcpClient = new TcpClient();
            await tcpClient.ConnectAsync(uri.Host, uri.Port);

            tcpClient.Client.NoDelay = true;
            var wrappedStreams = tcpClient.Client.CreateStreams();

            return(new Connection(
                       config, wrappedStreams.ReadableStream, wrappedStreams.WriteableStream));
        }
Exemple #12
0
        private void _serverSocket_OnAccepted(object obj)
        {
            var ci = (ChannelInfo)obj;

            if (ci != null)
            {
                var config = new ConnectionConfigurationBuilder(isServer: true)
                             .UseStreamListener(Http2Handler)
                             .UseHuffmanStrategy(HuffmanStrategy.Never)
                             .UseBufferPool(Buffers.Pool)
                             .Build();

                var wrappedStreams = ci.ClientSocket.CreateStreams();

                var http2Con = new Connection(config, wrappedStreams.ReadableStream, wrappedStreams.WriteableStream, options: new Connection.Options());
            }
        }
Exemple #13
0
        Connection BuildConnection(
            bool isServer,
            Settings settings,
            IReadableByteStream inputStream,
            IWriteAndCloseableByteStream outputStream)
        {
            var config =
                new ConnectionConfigurationBuilder(isServer)
                .UseSettings(settings)
                .UseStreamListener((s) => false)
                .Build();

            return(new Connection(
                       config, inputStream, outputStream,
                       new Connection.Options
            {
                Logger = loggerProvider.CreateLogger("http2Con"),
            }));
        }
Exemple #14
0
        public static async Task <Connection> BuildEstablishedConnection(
            bool isServer,
            IBufferedPipe inputStream,
            IBufferedPipe outputStream,
            ILoggerProvider loggerProvider,
            Func <IStream, bool> streamListener = null,
            Settings?localSettings          = null,
            Settings?remoteSettings         = null,
            HuffmanStrategy huffmanStrategy = HuffmanStrategy.Never)
        {
            ILogger logger = null;

            if (loggerProvider != null)
            {
                logger = loggerProvider.CreateLogger("http2Con");
            }
            if (streamListener == null)
            {
                streamListener = (s) => false;
            }

            var config = new ConnectionConfigurationBuilder(isServer)
                         .UseStreamListener(streamListener)
                         .UseHuffmanStrategy(huffmanStrategy)
                         .UseSettings(localSettings ?? Settings.Default)
                         .Build();

            var conn = new Connection(
                config, inputStream, outputStream,
                new Connection.Options
            {
                Logger = logger,
            });

            await PerformHandshakes(
                conn,
                inputStream, outputStream,
                remoteSettings);

            return(conn);
        }
Exemple #15
0
    static async Task AcceptTask(TcpListener listener, ILoggerProvider logProvider)
    {
        var connectionId = 0;

        var settings = Settings.Default;

        settings.MaxConcurrentStreams = 50;

        var config =
            new ConnectionConfigurationBuilder(true)
            .UseStreamListener(AcceptIncomingStream)
            .UseSettings(settings)
            .UseHuffmanStrategy(HuffmanStrategy.IfSmaller)
            .Build();

        var useUpgrade = true;

        while (true)
        {
            // Accept TCP sockets
            var clientSocket = await listener.AcceptSocketAsync();

            clientSocket.NoDelay = true;
            // Create HTTP/2 stream abstraction on top of the socket
            var wrappedStreams = clientSocket.CreateStreams();
            // Alternatively on top of a System.IO.Stream
            //var netStream = new NetworkStream(clientSocket, true);
            //var wrappedStreams = netStream.CreateStreams();
            await HandleConnection(
                logProvider,
                config,
                useUpgrade,
                wrappedStreams.ReadableStream,
                wrappedStreams.WriteableStream,
                connectionId);

            connectionId++;
        }
    }
Exemple #16
0
        private async Task HandleConnection(
            ILogger logger,
            ConnectionByteStream stream)
        {
            var config = new ConnectionConfigurationBuilder(true)
                         .UseStreamListener(arg =>
            {
                Task.Run(() =>
                {
                    var headers  = arg.ReadHeadersAsync().GetAwaiter().GetResult();
                    var optional = HttpRequest.ParseFrom(new HttpHeaderCollection(headers),
                                                         new HttpMethodResolver(Methods));
                    if (optional.ExpectPayload)
                    {
                        var seg = new ArraySegment <byte>(new byte[int.Parse(optional.Headers["content-length"])]);
                        // TODO: Use the StreamReadResult, as sometimes the packets won't all be sent at once
                        arg.ReadAsync(seg).GetAwaiter().GetResult();
                        optional.Payload = seg.ToArray();
                    }

                    var req = optional.Request;
                    PassContext(new HttpContext(req, new HttpTwoResponse(arg), stream.Connection, logger));
                });
                return(true);
            })
                         .UseSettings(Settings.Default)
                         .UseHuffmanStrategy(HuffmanStrategy.IfSmaller)
                         .Build();
            var upgradeReadStream           = new HttpStream(stream);
            ServerUpgradeRequest upgrade    = null;
            var writeAndCloseableByteStream = stream;

            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
                    upgradeReadStream.Unread();
                }
                else
                {
                    // This seems to be a HTTP/1 request
                    // Parse the header and check whether it's an upgrade
                    var req = HttpRequest.ParseFrom(
                        Encoding.ASCII.GetString(
                            headerBytes.Array, headerBytes.Offset, headerBytes.Count - 4),
                        (MethodResolver <HttpMethod>)MethodResolver, stream.Connection);

                    if (req.ExpectPayload && req.Headers.TryGetValue("content-length", out string contentLength))
                    {
                        if (int.TryParse(contentLength, out var length))
                        {
                            await upgradeReadStream.WaitForPayloadAsync(length);

                            req.Payload = upgradeReadStream.Payload.ToArray();
                        }
                        else
                        {
                            await stream.WriteAsync(
                                new ArraySegment <byte>(Encoding.ASCII.GetBytes(
                                                            "HTTP/1.1 400 Bad Request\r\nServer: SimpleServer/1.0 sshttp2\r\nContent-Type: text/html\r\nContent-Length: 26\r\n\r\nThe payload was too large.")));

                            await stream.CloseAsync();

                            return;
                        }
                    }
                    else if (req.ExpectPayload)
                    {
                        await stream.WriteAsync(
                            new ArraySegment <byte>(Encoding.ASCII.GetBytes(
                                                        "HTTP/1.1 400 Bad Request\r\nServer: SimpleServer/1.0 sshttp2\r\nContent-Type: text/html\r\nContent-Length: 25\r\n\r\nNo content-length header.")));

                        await stream.CloseAsync();

                        return;
                    }

                    // Assure that the HTTP/2 library does not get passed the HTTP/1 request
                    upgradeReadStream.Consume();

                    var request = req.Request;

#pragma warning disable 618
                    if (request.Protocol != "HTTP/1.1")
#pragma warning restore 618
                    {
                        PassContext(new HttpContext(request, new HttpOneResponse(request, stream.Connection),
                                                    stream.Connection, logger));
                        return;
                    }

                    if (!request.Headers.TryGetValue("connection", out string connectionHeader) ||
                        !request.Headers.TryGetValue("host", out string _) ||
                        !request.Headers.TryGetValue("upgrade", out string upgradeHeader) ||
                        !request.Headers.TryGetValue("http2-settings", out string http2SettingsHeader) ||
                        upgradeHeader != "h2c" ||
                        http2SettingsHeader.Length == 0)
                    {
                        // STAY H1
                        PassContext(new HttpContext(request, new HttpOneResponse(request, stream.Connection),
                                                    stream.Connection, logger));
                        return;
                    }

                    var connParts =
                        connectionHeader
                        .Split(',')
                        .Select(p => p.Trim())
                        .ToArray();
                    if (connParts.Length != 2 ||
                        !connParts.Contains("Upgrade") ||
                        !connParts.Contains("HTTP2-Settings"))
                    {
                        PassContext(new HttpContext(request, new HttpOneResponse(request, stream.Connection),
                                                    stream.Connection, logger));
                    }

                    var headers = new List <HeaderField>
                    {
                        new HeaderField {
                            Name = ":method", Value = request.Method.Id
                        },
                        new HeaderField {
                            Name = ":path", Value = request.RawUrl
                        },
                        new HeaderField
                        {
                            Name = ":scheme",
#if NETCOREAPP2_1 || NETSTANDARD2_1
                            Value = stream.Connection is SslListener.SecureConnection ? "https" : "http"
                            #else
                            Value = "http"
#endif
                        }
                    };
                    foreach (var kvp in request.Headers)
                    {
                        // Skip Connection upgrade related headers
                        if (kvp.Name == "connection" ||
                            kvp.Name == "upgrade" ||
                            kvp.Name == "http2-settings")
                        {
                            continue;
                        }
                        headers.Add(new HeaderField
                        {
                            Name  = kvp.Name,
                            Value = kvp.Value
                        });
                    }

                    var upgradeBuilder = new ServerUpgradeRequestBuilder();
                    upgradeBuilder.SetHeaders(headers);
                    upgradeBuilder.SetPayload(new ArraySegment <byte>(req.Payload));
                    upgradeBuilder.SetHttp2Settings(http2SettingsHeader);
                    upgrade = upgradeBuilder.Build();

                    if (!upgrade.IsValid)
                    {
                        // STAY H1
                        PassContext(new HttpContext(request, new HttpOneResponse(request, stream.Connection),
                                                    stream.Connection, logger));
                        return;
                    }

                    // Respond to upgrade
                    await writeAndCloseableByteStream.WriteAsync(
                        new ArraySegment <byte>(UpgradeSuccessResponse));
                }
            }
            catch (Exception e)
            {
                logger?.LogError(new EventId(stream.Connection.Id, "StartFail"), e,
                                 "An error occured while trying to upgrade a HTTP/1.X request to a H2 request.");
                await writeAndCloseableByteStream.CloseAsync();

                return;
            }

            // Build a H2 connection
            var http2Con = new Connection(
                config, upgradeReadStream, writeAndCloseableByteStream,
                new Connection.Options
            {
                Logger = logger,
                ServerUpgradeRequest = upgrade
            });

            // Close the connection if we get a GoAway from the client
            var remoteGoAwayTask = http2Con.RemoteGoAwayReason;
#pragma warning disable 4014
            Task.Run(async() =>
#pragma warning restore 4014
            {
                await remoteGoAwayTask;
                await http2Con.GoAwayAsync(ErrorCode.NoError, true);
            });
        }
Exemple #17
0
        public async Task TheNextOutgoingStreamAfterUpgradeShouldUseId3()
        {
            var inPipe  = new BufferedPipe(1024);
            var outPipe = new BufferedPipe(1024);

            var upgrade = new ClientUpgradeRequestBuilder().Build();
            var config  = new ConnectionConfigurationBuilder(false)
                          .Build();

            var conn = new Connection(
                config, inPipe, outPipe,
                new Connection.Options
            {
                Logger = loggerProvider.CreateLogger("http2Con"),
                ClientUpgradeRequest = upgrade,
            });

            await conn.PerformHandshakes(inPipe, outPipe);

            var stream = await upgrade.UpgradeRequestStream;

            Assert.Equal(1u, stream.Id);

            var readHeadersTask = stream.ReadHeadersAsync();

            Assert.False(readHeadersTask.IsCompleted);

            var nextStream = await conn.CreateStreamAsync(DefaultGetHeaders);

            await outPipe.ReadAndDiscardHeaders(3u, false);

            Assert.Equal(3u, nextStream.Id);
            Assert.True(stream != nextStream);
            Assert.Equal(StreamState.HalfClosedLocal, stream.State);
            Assert.Equal(StreamState.Open, nextStream.State);

            var hEncoder = new Http2.Hpack.Encoder();
            await inPipe.WriteHeaders(hEncoder, 3u, true, DefaultStatusHeaders);

            var nextStreamHeaders = await nextStream.ReadHeadersAsync();

            Assert.True(nextStreamHeaders.SequenceEqual(DefaultStatusHeaders));
            Assert.False(readHeadersTask.IsCompleted);
            Assert.Equal(StreamState.HalfClosedRemote, nextStream.State);
            Assert.Equal(StreamState.HalfClosedLocal, stream.State);
            Assert.Equal(2, conn.ActiveStreamCount);

            await nextStream.WriteAsync(new ArraySegment <byte>(new byte[0]), true);

            await outPipe.ReadAndDiscardData(3u, true, 0);

            Assert.Equal(StreamState.Closed, nextStream.State);
            Assert.Equal(1, conn.ActiveStreamCount);

            var headers2 = DefaultStatusHeaders.Append(
                new HeaderField()
            {
                Name = "hh", Value = "vv"
            });
            await inPipe.WriteHeaders(hEncoder, 1u, false, headers2);

            var streamHeaders = await readHeadersTask;

            Assert.True(streamHeaders.SequenceEqual(headers2));
            await inPipe.WriteData(1u, 10, 0, true);

            var data = await stream.ReadAllToArrayWithTimeout();

            Assert.True(data.Length == 10);
            Assert.Equal(StreamState.Closed, stream.State);
            Assert.Equal(0, conn.ActiveStreamCount);
        }
Exemple #18
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());
            }
        }
 public ConnectionConfiguration Parse(string connectionString)
 {
     var builder = new ConnectionConfigurationBuilder();
     Parse(connectionString, builder);
     return builder.Build();
 }
Exemple #20
0
        public async Task ServerUpgradeRequestsShouldDispatchStream1(
            int payloadLength)
        {
            var     inPipe            = new BufferedPipe(1024);
            var     outPipe           = new BufferedPipe(1024);
            int     nrAcceptedStreams = 0;
            IStream stream            = null;
            var     handlerDone       = new SemaphoreSlim(0);

            Func <IStream, bool> listener = (s) =>
            {
                Interlocked.Increment(ref nrAcceptedStreams);
                Task.Run(() =>
                {
                    stream = s;
                    handlerDone.Release();
                });
                return(true);
            };

            var startOfPayload = 44;

            byte[] payload = new byte[payloadLength];
            for (var i = 0; i < payloadLength; i++)
            {
                payload[i] = (byte)(startOfPayload + i);
            }

            var builder = new ServerUpgradeRequestBuilder();
            var headers = DefaultGetHeaders.ToList();

            if (payloadLength != 0)
            {
                builder.SetPayload(new ArraySegment <byte>(payload));
                headers.Add(new HeaderField()
                {
                    Name  = "content-length",
                    Value = payloadLength.ToString()
                });
            }
            builder.SetHeaders(headers);
            builder.SetHttp2Settings("");
            var upgrade = builder.Build();

            var config = new ConnectionConfigurationBuilder(true)
                         .UseStreamListener(listener)
                         .Build();

            var conn = new Connection(
                config, inPipe, outPipe,
                new Connection.Options
            {
                Logger = loggerProvider.CreateLogger("http2Con"),
                ServerUpgradeRequest = upgrade,
            });

            await conn.PerformHandshakes(inPipe, outPipe);

            var requestDone = await handlerDone.WaitAsync(
                ReadableStreamTestExtensions.ReadTimeout);

            Assert.True(requestDone, "Expected handler to complete within timeout");

            Assert.Equal(1u, stream.Id);
            Assert.Equal(StreamState.HalfClosedRemote, stream.State);
            var rcvdHeaders = await stream.ReadHeadersAsync();

            Assert.True(headers.SequenceEqual(rcvdHeaders));
            var allData = await stream.ReadAllToArrayWithTimeout();

            Assert.Equal(payloadLength, allData.Length);
            Assert.Equal(payload, allData);

            Assert.Equal(1, nrAcceptedStreams);
        }
Exemple #21
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();
            }
        }
    }
Exemple #22
0
        public override AsyncUnaryCall <TResponse> AsyncUnaryCall <TRequest, TResponse>(Method <TRequest, TResponse> method, string host, CallOptions options, TRequest request)
        {
            // The HTTP/2 gRPC protocol is defined here
            // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
            // When debugging, be aware of the timeouts!
            // You can get some insights about what's going on by running kubectl logs <tiller> -n kube-system -f
            ConnectionConfiguration config =
                new ConnectionConfigurationBuilder(isServer: false)
                .Build();

            using (Stream networkStream = this.stream())
            {
                var streams = networkStream.CreateStreams();

                Connection http2Connection = new Connection(
                    config: config,
                    inputStream: streams.ReadableStream,
                    outputStream: streams.WriteableStream);

                var headers = new Collection <HeaderField>
                {
                    new HeaderField {
                        Name = ":method", Value = "POST"
                    },
                    new HeaderField {
                        Name = ":scheme", Value = "http"
                    },
                    new HeaderField {
                        Name = ":path", Value = method.FullName
                    },
                    new HeaderField {
                        Name = ":authority", Value = "pubsub.googleapis.com"
                    },
                    new HeaderField {
                        Name = "grpc-timeout", Value = "60S"
                    },
                    new HeaderField {
                        Name = "content-type", Value = "application/grpc+proto"
                    }
                };

                if (options.Headers != null)
                {
                    foreach (var header in options.Headers)
                    {
                        headers.Add(new HeaderField()
                        {
                            Name  = header.Key,
                            Value = header.Value
                        });
                    }
                }

                var stream = http2Connection.CreateStreamAsync(
                    headers, endOfStream: false).GetAwaiter().GetResult();

                var    requestMessage = request as IMessage;
                byte[] buffer         = new byte[4];

                stream.WriteAsync(new ArraySegment <byte>(buffer, 0, 1), endOfStream: false);
                int size = requestMessage.CalculateSize();
                buffer = BitConverter.GetBytes(size);
                Array.Reverse(buffer);
                stream.WriteAsync(new ArraySegment <byte>(buffer, 0, 4), endOfStream: size == 0);

                if (size > 0)
                {
                    buffer = requestMessage.ToByteArray();
                    stream.WriteAsync(new ArraySegment <byte>(buffer, 0, buffer.Length), endOfStream: true);
                }

                // Wait for response headers
                var responseHeaders = stream.ReadHeadersAsync().GetAwaiter().GetResult();

                if (this.logger != null)
                {
                    foreach (var header in responseHeaders)
                    {
                        this.logger.LogTrace("{header.Name} = {header.Value}");
                    }
                }

                var statusCode           = responseHeaders.SingleOrDefault(h => h.Name == ":status").Value;
                var contentType          = responseHeaders.SingleOrDefault(h => h.Name == "content-type").Value;
                var grpcStatusCodeString = responseHeaders.SingleOrDefault(h => h.Name == "grpc-status").Value;
                var grpcMessage          = responseHeaders.SingleOrDefault(h => h.Name == "grpc-message").Value;

                var grpcStatusCode = grpcStatusCodeString == null ? StatusCode.OK : (StatusCode)Enum.Parse(typeof(StatusCode), grpcStatusCodeString);

                // Read response data
                var response = Activator.CreateInstance <TResponse>();

                using (MemoryStream ms = new MemoryStream())
                {
                    // See https://github.com/Matthias247/http2dotnet/issues/1
                    var streamImplType        = typeof(IStream).Assembly.GetType("Http2.StreamImpl");
                    var readDataPossibleField = streamImplType.GetField("readDataPossible", BindingFlags.NonPublic | BindingFlags.Instance);
                    var readDataPossible      = (AsyncManualResetEvent)readDataPossibleField.GetValue(stream);
                    readDataPossible.Set();

                    buffer = new byte[1024];
                    while (true)
                    {
                        var readDataResult = stream.ReadAsync(new ArraySegment <byte>(buffer)).GetAwaiter().GetResult();
                        ms.Write(buffer, 0, readDataResult.BytesRead);

                        if (readDataResult.EndOfStream)
                        {
                            break;
                        }
                    }

                    var responseMessage = response as IMessage;

                    ms.Position = 0;
                    bool   isCompressed = ms.ReadByte() != 0;
                    byte[] lengthBuffer = new byte[4];
                    ms.Read(lengthBuffer, 0, 4);
                    Array.Reverse(lengthBuffer);
                    var length = BitConverter.ToUInt32(lengthBuffer, 0);
                    this.logger?.LogTrace("Read {length} bytes of data");

                    responseMessage.MergeFrom(ms);
                }

                var responseTrailers = stream.ReadTrailersAsync().GetAwaiter().GetResult();

                if (grpcStatusCode != StatusCode.OK)
                {
                    var status   = new Status(grpcStatusCode, grpcMessage);
                    var metadata = new Metadata();

                    foreach (var trailer in responseTrailers)
                    {
                        metadata.Add(new Metadata.Entry(trailer.Name, trailer.Value));
                    }

                    throw new Grpc.Core.RpcException(status, metadata);
                }

                return(new AsyncUnaryCall <TResponse>(Task.FromResult(response), Task.FromResult((Metadata)null), () => Status.DefaultSuccess, null, null));
            }
        }