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 }
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()); }
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()); }
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()); }
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()); }
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); }
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()); }
public void SetMaxCumulationBufferComponentsAfterInit() { var aggregator = new HttpObjectAggregator(int.MaxValue); var ch = new EmbeddedChannel(aggregator); Assert.Throws <InvalidOperationException>(() => aggregator.MaxCumulationBufferComponents = 10); Assert.False(ch.Finish()); }
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()); }
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()); }
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()); }
public void InvalidMaxCumulationBufferComponents() { var aggregator = new HttpObjectAggregator(int.MaxValue); Assert.Throws <ArgumentException>(() => aggregator.MaxCumulationBufferComponents = 1); }
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); }