示例#1
0
        public void RequestDecompression()
        {
            // baseline test: request decoder, content decompressor && request aggregator work as expected
            var decoder      = new HttpRequestDecoder();
            var decompressor = new HttpContentDecompressor();
            var aggregator   = new HttpObjectAggregator(1024);
            var channel      = new EmbeddedChannel(decoder, decompressor, aggregator);

            string headers = "POST / HTTP/1.1\r\n" +
                             "Content-Length: " + GzHelloWorld.Length + "\r\n" +
                             "Content-Encoding: gzip\r\n" +
                             "\r\n";
            IByteBuffer buf = Unpooled.CopiedBuffer(Encoding.ASCII.GetBytes(headers), GzHelloWorld);

            Assert.True(channel.WriteInbound(buf));

            var req = channel.ReadInbound <IFullHttpRequest>();

            Assert.NotNull(req);
            Assert.True(req.Headers.TryGetInt(HttpHeaderNames.ContentLength, out int length));
            Assert.Equal(HelloWorld.Length, length);
            Assert.Equal(HelloWorld, req.Content.ToString(Encoding.ASCII));
            req.Release();

            AssertHasInboundMessages(channel, false);
            AssertHasOutboundMessages(channel, false);
            Assert.False(channel.Finish()); // assert that no messages are left in channel
        }
示例#2
0
        public void FullHttpResponse()
        {
            // test that ContentDecoder can be used after the ObjectAggregator
            var    decoder      = new HttpResponseDecoder(4096, 4096, 5);
            var    aggregator   = new HttpObjectAggregator(1024);
            var    decompressor = new HttpContentDecompressor();
            var    channel      = new EmbeddedChannel(decoder, aggregator, decompressor);
            string headers      = "HTTP/1.1 200 OK\r\n" +
                                  "Content-Length: " + GzHelloWorld.Length + "\r\n" +
                                  "Content-Encoding: gzip\r\n" +
                                  "\r\n";

            Assert.True(channel.WriteInbound(Unpooled.CopiedBuffer(Encoding.ASCII.GetBytes(headers), GzHelloWorld)));

            var resp = channel.InboundMessages;

            Assert.True(resp.Count > 1);
            int contentLength = 0;

            contentLength = CalculateContentLength(resp, contentLength);
            byte[] receivedContent = ReadContent(resp, contentLength, true);
            Assert.Equal(HelloWorld, Encoding.ASCII.GetString(receivedContent));

            AssertHasInboundMessages(channel, true);
            AssertHasOutboundMessages(channel, false);
            Assert.False(channel.Finish());
        }
示例#3
0
        public void ExpectContinueResponse1()
        {
            // request with header "Expect: 100-continue" must be replied with one "100 Continue" response
            // case 1: no ContentDecoder in chain at all (baseline test)
            var    decoder    = new HttpRequestDecoder();
            var    aggregator = new HttpObjectAggregator(1024);
            var    channel    = new EmbeddedChannel(decoder, aggregator);
            string req        = "POST / HTTP/1.1\r\n" +
                                "Content-Length: " + GzHelloWorld.Length + "\r\n" +
                                "Expect: 100-continue\r\n" +
                                "\r\n";

            // note: the following writeInbound() returns false as there is no message is inbound buffer
            // until HttpObjectAggregator caches composes a complete message.
            // however, http response "100 continue" must be sent as soon as headers are received
            Assert.False(channel.WriteInbound(Unpooled.WrappedBuffer(Encoding.ASCII.GetBytes(req))));

            var resp = channel.ReadOutbound <IFullHttpResponse>();

            Assert.NotNull(resp);
            Assert.Equal(100, resp.Status.Code);
            Assert.True(channel.WriteInbound(Unpooled.CopiedBuffer(GzHelloWorld)));
            resp.Release();

            AssertHasInboundMessages(channel, true);
            AssertHasOutboundMessages(channel, false);
            Assert.False(channel.Finish());
        }
示例#4
0
        public void ResponseContentLength2()
        {
            // case 2: if HttpObjectAggregator is down the chain, then correct Content - Length header must be set

            // force content to be in more than one chunk (5 bytes/chunk)
            var    decoder      = new HttpResponseDecoder(4096, 4096, 5);
            var    decompressor = new HttpContentDecompressor();
            var    aggregator   = new HttpObjectAggregator(1024);
            var    channel      = new EmbeddedChannel(decoder, decompressor, aggregator);
            string headers      = "HTTP/1.1 200 OK\r\n" +
                                  "Content-Length: " + GzHelloWorld.Length + "\r\n" +
                                  "Content-Encoding: gzip\r\n" +
                                  "\r\n";
            IByteBuffer buf = Unpooled.CopiedBuffer(Encoding.ASCII.GetBytes(headers), GzHelloWorld);

            Assert.True(channel.WriteInbound(buf));

            var res = channel.ReadInbound <IFullHttpResponse>();

            Assert.NotNull(res);
            Assert.True(res.Headers.TryGet(HttpHeaderNames.ContentLength, out ICharSequence value));
            Assert.Equal(HelloWorld.Length, long.Parse(value.ToString()));
            res.Release();

            AssertHasInboundMessages(channel, false);
            AssertHasOutboundMessages(channel, false);
            Assert.False(channel.Finish());
        }
示例#5
0
        public void ExpectContinueResponse4()
        {
            // request with header "Expect: 100-continue" must be replied with one "100 Continue" response
            // case 4: ObjectAggregator is up in chain
            var    decoder      = new HttpRequestDecoder();
            var    aggregator   = new HttpObjectAggregator(1024);
            var    decompressor = new HttpContentDecompressor();
            var    channel      = new EmbeddedChannel(decoder, aggregator, decompressor);
            string req          = "POST / HTTP/1.1\r\n" +
                                  "Content-Length: " + GzHelloWorld.Length + "\r\n" +
                                  "Expect: 100-continue\r\n" +
                                  "Content-Encoding: gzip\r\n" +
                                  "\r\n";

            Assert.False(channel.WriteInbound(Unpooled.WrappedBuffer(Encoding.ASCII.GetBytes(req))));

            var resp = channel.ReadOutbound <IFullHttpResponse>();

            Assert.NotNull(resp);
            Assert.Equal(100, resp.Status.Code);
            resp.Release();
            Assert.True(channel.WriteInbound(Unpooled.CopiedBuffer(GzHelloWorld)));

            AssertHasInboundMessages(channel, true);
            AssertHasOutboundMessages(channel, false);
            Assert.False(channel.Finish());
        }
示例#6
0
        public void AggregateWithTrailer()
        {
            var aggregator = new HttpObjectAggregator(1024 * 1024);
            var ch         = new EmbeddedChannel(aggregator);
            var message    = new DefaultHttpRequest(HttpVersion.Http11, HttpMethod.Get, "http://localhost");

            message.Headers.Set((AsciiString)"X-Test", true);
            HttpUtil.SetTransferEncodingChunked(message, true);
            var chunk1  = new DefaultHttpContent(Unpooled.CopiedBuffer(Encoding.ASCII.GetBytes("test")));
            var chunk2  = new DefaultHttpContent(Unpooled.CopiedBuffer(Encoding.ASCII.GetBytes("test2")));
            var trailer = new DefaultLastHttpContent();

            trailer.TrailingHeaders.Set((AsciiString)"X-Trailer", true);

            Assert.False(ch.WriteInbound(message));
            Assert.False(ch.WriteInbound(chunk1));
            Assert.False(ch.WriteInbound(chunk2));

            // this should trigger a channelRead event so return true
            Assert.True(ch.WriteInbound(trailer));
            Assert.True(ch.Finish());
            var aggregatedMessage = ch.ReadInbound <IFullHttpRequest>();

            Assert.NotNull(aggregatedMessage);

            Assert.Equal(chunk1.Content.ReadableBytes + chunk2.Content.ReadableBytes, HttpUtil.GetContentLength(aggregatedMessage));
            Assert.Equal(bool.TrueString, aggregatedMessage.Headers.Get((AsciiString)"X-Test", null)?.ToString());
            Assert.Equal(bool.TrueString, aggregatedMessage.TrailingHeaders.Get((AsciiString)"X-Trailer", null)?.ToString());
            CheckContentBuffer(aggregatedMessage);
            var last = ch.ReadInbound <object>();

            Assert.Null(last);
        }
示例#7
0
        public void OversizedRequest()
        {
            var aggregator = new HttpObjectAggregator(4);
            var ch         = new EmbeddedChannel(aggregator);
            var message    = new DefaultHttpRequest(HttpVersion.Http11, HttpMethod.Put, "http://localhost");
            var chunk1     = new DefaultHttpContent(Unpooled.CopiedBuffer(Encoding.ASCII.GetBytes("test")));
            var chunk2     = new DefaultHttpContent(Unpooled.CopiedBuffer(Encoding.ASCII.GetBytes("test2")));
            EmptyLastHttpContent chunk3 = EmptyLastHttpContent.Default;

            Assert.False(ch.WriteInbound(message));
            Assert.False(ch.WriteInbound(chunk1));
            Assert.False(ch.WriteInbound(chunk2));

            var response = ch.ReadOutbound <IFullHttpResponse>();

            Assert.Equal(HttpResponseStatus.RequestEntityTooLarge, response.Status);
            Assert.Equal("0", response.Headers.Get(HttpHeaderNames.ContentLength, null));
            Assert.False(ch.Open);

            try
            {
                Assert.False(ch.WriteInbound(chunk3));
                Assert.True(false, "Shoud not get here, expecting exception thrown.");
            }
            catch (Exception e)
            {
                Assert.True(e is ClosedChannelException);
            }

            Assert.False(ch.Finish());
        }
示例#8
0
        public void SetMaxCumulationBufferComponentsAfterInit()
        {
            var aggregator = new HttpObjectAggregator(int.MaxValue);
            var ch         = new EmbeddedChannel(aggregator);

            Assert.Throws <InvalidOperationException>(() => aggregator.MaxCumulationBufferComponents = 10);
            Assert.False(ch.Finish());
        }
示例#9
0
        public void OversizedResponse()
        {
            var aggregator = new HttpObjectAggregator(4);
            var ch         = new EmbeddedChannel(aggregator);
            var message    = new DefaultHttpResponse(HttpVersion.Http11, HttpResponseStatus.OK);
            var chunk1     = new DefaultHttpContent(Unpooled.CopiedBuffer(Encoding.ASCII.GetBytes("test")));
            var chunk2     = new DefaultHttpContent(Unpooled.CopiedBuffer(Encoding.ASCII.GetBytes("test2")));

            Assert.False(ch.WriteInbound(message));
            Assert.False(ch.WriteInbound(chunk1));
            Assert.Throws <TooLongFrameException>(() => ch.WriteInbound(chunk2));

            Assert.False(ch.Open);
            Assert.False(ch.Finish());
        }
示例#10
0
        public void ExpectContinueResetHttpObjectDecoder()
        {
            // request with header "Expect: 100-continue" must be replied with one "100 Continue" response
            // case 5: Test that HttpObjectDecoder correctly resets its internal state after a failed expectation.
            var       decoder    = new HttpRequestDecoder();
            const int MaxBytes   = 10;
            var       aggregator = new HttpObjectAggregator(MaxBytes);

            var    testHandler = new TestHandler();
            var    channel     = new EmbeddedChannel(decoder, aggregator, testHandler);
            string req1        = "POST /1 HTTP/1.1\r\n" +
                                 "Content-Length: " + (MaxBytes + 1) + "\r\n" +
                                 "Expect: 100-continue\r\n" +
                                 "\r\n";

            Assert.False(channel.WriteInbound(Unpooled.WrappedBuffer(Encoding.ASCII.GetBytes(req1))));

            var resp = channel.ReadOutbound <IFullHttpResponse>();

            Assert.Equal(HttpStatusClass.ClientError, resp.Status.CodeClass);
            resp.Release();

            string req2 = "POST /2 HTTP/1.1\r\n" +
                          "Content-Length: " + MaxBytes + "\r\n" +
                          "Expect: 100-continue\r\n" +
                          "\r\n";

            Assert.False(channel.WriteInbound(Unpooled.WrappedBuffer(Encoding.ASCII.GetBytes(req2))));

            resp = channel.ReadOutbound <IFullHttpResponse>();
            Assert.Equal(100, resp.Status.Code);
            resp.Release();

            var content = new byte[MaxBytes];

            Assert.False(channel.WriteInbound(Unpooled.WrappedBuffer(content)));

            IFullHttpRequest req = testHandler.Request;

            Assert.NotNull(req);
            Assert.Equal("/2", req.Uri);
            Assert.Equal(10, req.Content.ReadableBytes);
            req.Release();

            AssertHasInboundMessages(channel, false);
            AssertHasOutboundMessages(channel, false);
            Assert.False(channel.Finish());
        }
示例#11
0
        public void UnsupportedExpectHeaderExpectation(bool close)
        {
            int maxContentLength = 4;
            var aggregator       = new HttpObjectAggregator(maxContentLength, close);
            var ch = new EmbeddedChannel(new HttpRequestDecoder(), aggregator);

            Assert.False(ch.WriteInbound(Unpooled.CopiedBuffer(Encoding.ASCII.GetBytes(
                                                                   "GET / HTTP/1.1\r\n" +
                                                                   "Expect: chocolate=yummy\r\n" +
                                                                   "Content-Length: 100\r\n\r\n"))));
            var next = ch.ReadInbound <object>();

            Assert.Null(next);

            var response = ch.ReadOutbound <IFullHttpResponse>();

            Assert.Equal(HttpResponseStatus.ExpectationFailed, response.Status);
            Assert.Equal((AsciiString)"0", response.Headers.Get(HttpHeaderNames.ContentLength, null));
            response.Release();

            if (close)
            {
                Assert.False(ch.Open);
            }
            else
            {
                // keep-alive is on by default in HTTP/1.1, so the connection should be still alive
                Assert.True(ch.Open);

                // the decoder should be reset by the aggregator at this point and be able to decode the next request
                Assert.True(ch.WriteInbound(Unpooled.CopiedBuffer(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n\r\n"))));

                var request = ch.ReadInbound <IFullHttpRequest>();
                Assert.NotNull(request);
                Assert.Equal(HttpMethod.Get, request.Method);
                Assert.Equal("/", request.Uri);
                Assert.Equal(0, request.Content.ReadableBytes);
                request.Release();
            }

            Assert.False(ch.Finish());
        }
示例#12
0
        public void InvalidMaxCumulationBufferComponents()
        {
            var aggregator = new HttpObjectAggregator(int.MaxValue);

            Assert.Throws <ArgumentException>(() => aggregator.MaxCumulationBufferComponents = 1);
        }
示例#13
0
        private void InitDotNetty()
        {
            _responseHandler = new HttpResponseHandler();

            if (_proxyCredentials != null)
            {
                NetworkCredential credential = _proxyCredentials.GetCredential(_uri, "Basic");

                //TODO 除了Basic的其它实现
                string authString = !string.IsNullOrEmpty(credential.Domain) ?
                                    credential.Domain + "\\" + credential.UserName + ":" + credential.Password :
                                    credential.UserName + ":" + credential.Password;

                _authenticationString = new StringCharSequence($"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes(authString))}");

                _requestSetupFunc = request =>
                                    request.Headers.Remove(HttpHeaderNames.ProxyAuthorization).Add(HttpHeaderNames.ProxyAuthorization, _authenticationString);
            }

            _group     = new SingleThreadEventLoop();
            _bootstrap = new Bootstrap();
            _bootstrap
            .Group(_group)
            .Option(ChannelOption.TcpNodelay, true)
            .Option(ChannelOption.SoKeepalive, true)
            .Option(ChannelOption.Allocator, new UnpooledByteBufferAllocator())
            .Channel <TcpSocketChannel>()
            .Handler(new ActionChannelInitializer <IChannel>(channel =>
            {
                IChannelPipeline pipeline = channel.Pipeline;

                var aggregatorHandler = new HttpObjectAggregator(_maxLength);
                var codecHandler      = new HttpClientCodec(4096, 8192, 8192, false, false, _useProxy);

                if (IsHttps)
                {
                    if (_proxyUri is null)
                    {
                        pipeline.AddTlsHandler(Host, _certificateValidationCallback);
                        pipeline.AddLast(DotNettyHandlerNames.Codec, codecHandler);
                        pipeline.AddLast(DotNettyHandlerNames.Aggregator, aggregatorHandler);
                    }
                    else
                    {
                        pipeline.AddLast(DotNettyHandlerNames.Codec, codecHandler);
                        pipeline.AddLast(DotNettyHandlerNames.Aggregator, aggregatorHandler);
                        _connectionUpgradeHandler = new ConnectionUpgradeHandler(Host, _certificateValidationCallback);
                        pipeline.AddLast(DotNettyHandlerNames.ConnectionUpgrade, _connectionUpgradeHandler);
                    }
                }
                else
                {
                    pipeline.AddLast(DotNettyHandlerNames.Codec, codecHandler);
                    pipeline.AddLast(DotNettyHandlerNames.Aggregator, aggregatorHandler);
                }

                pipeline.AddLast(DotNettyHandlerNames.Response, _responseHandler);

                _dotNettyPipelineSetupCallback?.Invoke(pipeline);
            }));

            _dotNettyBootstrapSetupCallback?.Invoke(_bootstrap);
        }