Skip to content

Commit 65edd78

Browse files
committed
Support http read timeouts for transport-nio (elastic#41466)
This is related to elastic#27260. Currently there is a setting http.read_timeout that allows users to define a read timeout for the http transport. This commit implements support for this functionality with the transport-nio plugin. The behavior here is that a repeating task will be scheduled for the interval defined. If there have been no requests received since the last run and there are no inflight requests, the channel will be closed.
1 parent c86f797 commit 65edd78

File tree

17 files changed

+263
-107
lines changed

17 files changed

+263
-107
lines changed

libs/nio/src/main/java/org/elasticsearch/nio/BytesWriteHandler.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public WriteOperation createWriteOperation(SocketChannelContext context, Object
3434
return new FlushReadyWrite(context, (ByteBuffer[]) message, listener);
3535
}
3636

37+
@Override
38+
public void channelRegistered() {}
39+
3740
@Override
3841
public List<FlushOperation> writeToBytes(WriteOperation writeOperation) {
3942
assert writeOperation instanceof FlushReadyWrite : "Write operation must be flush ready";

libs/nio/src/main/java/org/elasticsearch/nio/ReadWriteHandler.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
*/
2929
public interface ReadWriteHandler {
3030

31+
/**
32+
* This method is called when the channel is registered with its selector.
33+
*/
34+
void channelRegistered();
35+
3136
/**
3237
* This method is called when a message is queued with a channel. It can be called from any thread.
3338
* This method should validate that the message is a valid type and return a write operation object

libs/nio/src/main/java/org/elasticsearch/nio/SocketChannelContext.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ protected FlushOperation getPendingFlush() {
169169
@Override
170170
protected void register() throws IOException {
171171
super.register();
172+
readWriteHandler.channelRegistered();
172173
if (allowChannelPredicate.test(channel) == false) {
173174
closeNow = true;
174175
}

libs/nio/src/main/java/org/elasticsearch/nio/TaskScheduler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public Runnable scheduleAtRelativeTime(Runnable task, long relativeNanos) {
4545
return delayedTask;
4646
}
4747

48-
Runnable pollTask(long relativeNanos) {
48+
public Runnable pollTask(long relativeNanos) {
4949
DelayedTask task;
5050
while ((task = tasks.peek()) != null) {
5151
if (relativeNanos - task.deadline >= 0) {

modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpServerTransport.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
import org.apache.logging.log4j.Logger;
4646
import org.elasticsearch.ExceptionsHelper;
4747
import org.elasticsearch.common.Strings;
48-
import org.elasticsearch.common.network.CloseableChannel;
4948
import org.elasticsearch.common.network.NetworkService;
5049
import org.elasticsearch.common.settings.Setting;
5150
import org.elasticsearch.common.settings.Setting.Property;
@@ -59,6 +58,7 @@
5958
import org.elasticsearch.http.AbstractHttpServerTransport;
6059
import org.elasticsearch.http.HttpChannel;
6160
import org.elasticsearch.http.HttpHandlingSettings;
61+
import org.elasticsearch.http.HttpReadTimeoutException;
6262
import org.elasticsearch.http.HttpServerChannel;
6363
import org.elasticsearch.http.netty4.cors.Netty4CorsConfig;
6464
import org.elasticsearch.http.netty4.cors.Netty4CorsConfigBuilder;
@@ -289,12 +289,9 @@ protected void stopInternal() {
289289
}
290290

291291
@Override
292-
protected void onException(HttpChannel channel, Exception cause) {
292+
public void onException(HttpChannel channel, Exception cause) {
293293
if (cause instanceof ReadTimeoutException) {
294-
if (logger.isTraceEnabled()) {
295-
logger.trace("Http read timeout {}", channel);
296-
}
297-
CloseableChannel.closeChannel(channel);
294+
super.onException(channel, new HttpReadTimeoutException(readTimeoutMillis, cause));
298295
} else {
299296
super.onException(channel, cause);
300297
}

modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@
7373
import java.util.Collections;
7474
import java.util.HashSet;
7575
import java.util.Set;
76+
import java.util.concurrent.CountDownLatch;
7677
import java.util.concurrent.TimeUnit;
77-
import java.util.concurrent.atomic.AtomicBoolean;
7878
import java.util.concurrent.atomic.AtomicReference;
7979
import java.util.regex.PatternSyntaxException;
8080
import java.util.stream.Collectors;
@@ -346,7 +346,7 @@ public void dispatchBadRequest(final RestRequest request,
346346
transport.start();
347347
final TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses());
348348

349-
AtomicBoolean channelClosed = new AtomicBoolean(false);
349+
CountDownLatch channelClosedLatch = new CountDownLatch(1);
350350

351351
Bootstrap clientBootstrap = new Bootstrap().channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
352352

@@ -357,9 +357,9 @@ protected void initChannel(SocketChannel ch) {
357357
}
358358
}).group(group);
359359
ChannelFuture connect = clientBootstrap.connect(remoteAddress.address());
360-
connect.channel().closeFuture().addListener(future -> channelClosed.set(true));
360+
connect.channel().closeFuture().addListener(future -> channelClosedLatch.countDown());
361361

362-
assertBusy(() -> assertTrue("Channel should be closed due to read timeout", channelClosed.get()), 5, TimeUnit.SECONDS);
362+
assertTrue("Channel should be closed due to read timeout", channelClosedLatch.await(1, TimeUnit.MINUTES));
363363

364364
} finally {
365365
group.shutdownGracefully().await();

plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/HttpReadWriteHandler.java

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,31 +30,45 @@
3030
import io.netty.handler.codec.http.HttpRequestDecoder;
3131
import io.netty.handler.codec.http.HttpResponseEncoder;
3232
import org.elasticsearch.ExceptionsHelper;
33+
import org.elasticsearch.common.unit.TimeValue;
3334
import org.elasticsearch.http.HttpHandlingSettings;
3435
import org.elasticsearch.http.HttpPipelinedRequest;
36+
import org.elasticsearch.http.HttpReadTimeoutException;
3537
import org.elasticsearch.http.nio.cors.NioCorsConfig;
3638
import org.elasticsearch.http.nio.cors.NioCorsHandler;
3739
import org.elasticsearch.nio.FlushOperation;
3840
import org.elasticsearch.nio.InboundChannelBuffer;
3941
import org.elasticsearch.nio.ReadWriteHandler;
4042
import org.elasticsearch.nio.SocketChannelContext;
43+
import org.elasticsearch.nio.TaskScheduler;
4144
import org.elasticsearch.nio.WriteOperation;
4245

4346
import java.io.IOException;
4447
import java.util.ArrayList;
4548
import java.util.List;
49+
import java.util.concurrent.TimeUnit;
4650
import java.util.function.BiConsumer;
51+
import java.util.function.LongSupplier;
4752

4853
public class HttpReadWriteHandler implements ReadWriteHandler {
4954

5055
private final NettyAdaptor adaptor;
5156
private final NioHttpChannel nioHttpChannel;
5257
private final NioHttpServerTransport transport;
58+
private final TaskScheduler taskScheduler;
59+
private final LongSupplier nanoClock;
60+
private final long readTimeoutNanos;
61+
private boolean channelRegistered = false;
62+
private boolean requestSinceReadTimeoutTrigger = false;
63+
private int inFlightRequests = 0;
5364

5465
public HttpReadWriteHandler(NioHttpChannel nioHttpChannel, NioHttpServerTransport transport, HttpHandlingSettings settings,
55-
NioCorsConfig corsConfig) {
66+
NioCorsConfig corsConfig, TaskScheduler taskScheduler, LongSupplier nanoClock) {
5667
this.nioHttpChannel = nioHttpChannel;
5768
this.transport = transport;
69+
this.taskScheduler = taskScheduler;
70+
this.nanoClock = nanoClock;
71+
this.readTimeoutNanos = TimeUnit.MILLISECONDS.toNanos(settings.getReadTimeoutMillis());
5872

5973
List<ChannelHandler> handlers = new ArrayList<>(5);
6074
HttpRequestDecoder decoder = new HttpRequestDecoder(settings.getMaxInitialLineLength(), settings.getMaxHeaderSize(),
@@ -77,10 +91,21 @@ public HttpReadWriteHandler(NioHttpChannel nioHttpChannel, NioHttpServerTranspor
7791
}
7892

7993
@Override
80-
public int consumeReads(InboundChannelBuffer channelBuffer) throws IOException {
94+
public void channelRegistered() {
95+
channelRegistered = true;
96+
if (readTimeoutNanos > 0) {
97+
scheduleReadTimeout();
98+
}
99+
}
100+
101+
@Override
102+
public int consumeReads(InboundChannelBuffer channelBuffer) {
103+
assert channelRegistered : "channelRegistered should have been called";
81104
int bytesConsumed = adaptor.read(channelBuffer.sliceAndRetainPagesTo(channelBuffer.getIndex()));
82105
Object message;
83106
while ((message = adaptor.pollInboundMessage()) != null) {
107+
++inFlightRequests;
108+
requestSinceReadTimeoutTrigger = true;
84109
handleRequest(message);
85110
}
86111

@@ -96,6 +121,11 @@ public WriteOperation createWriteOperation(SocketChannelContext context, Object
96121

97122
@Override
98123
public List<FlushOperation> writeToBytes(WriteOperation writeOperation) {
124+
assert writeOperation.getObject() instanceof NioHttpResponse : "This channel only supports messages that are of type: "
125+
+ NioHttpResponse.class + ". Found type: " + writeOperation.getObject().getClass() + ".";
126+
assert channelRegistered : "channelRegistered should have been called";
127+
--inFlightRequests;
128+
assert inFlightRequests >= 0 : "Inflight requests should never drop below zero, found: " + inFlightRequests;
99129
adaptor.write(writeOperation);
100130
return pollFlushOperations();
101131
}
@@ -152,4 +182,17 @@ private void handleRequest(Object msg) {
152182
request.release();
153183
}
154184
}
185+
186+
private void maybeReadTimeout() {
187+
if (requestSinceReadTimeoutTrigger == false && inFlightRequests == 0) {
188+
transport.onException(nioHttpChannel, new HttpReadTimeoutException(TimeValue.nsecToMSec(readTimeoutNanos)));
189+
} else {
190+
requestSinceReadTimeoutTrigger = false;
191+
scheduleReadTimeout();
192+
}
193+
}
194+
195+
private void scheduleReadTimeout() {
196+
taskScheduler.scheduleAtRelativeTime(this::maybeReadTimeout, nanoClock.getAsLong() + readTimeoutNanos);
197+
}
155198
}

plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/NioHttpServerTransport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ public NioHttpChannel createChannel(NioSelector selector, SocketChannel channel)
211211
return new Page(ByteBuffer.wrap(bytes.v()), bytes::close);
212212
};
213213
HttpReadWriteHandler httpReadWritePipeline = new HttpReadWriteHandler(httpChannel,NioHttpServerTransport.this,
214-
handlingSettings, corsConfig);
214+
handlingSettings, corsConfig, selector.getTaskScheduler(), threadPool::relativeTimeInMillis);
215215
Consumer<Exception> exceptionHandler = (e) -> onException(httpChannel, e);
216216
SocketChannelContext context = new BytesChannelContext(httpChannel, selector, exceptionHandler, httpReadWritePipeline,
217217
new InboundChannelBuffer(pageSupplier));

plugins/transport-nio/src/test/java/org/elasticsearch/http/nio/HttpReadWriteHandlerTests.java

Lines changed: 72 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,13 @@
3333
import io.netty.handler.codec.http.HttpResponseStatus;
3434
import io.netty.handler.codec.http.HttpUtil;
3535
import io.netty.handler.codec.http.HttpVersion;
36-
3736
import org.elasticsearch.common.bytes.BytesArray;
3837
import org.elasticsearch.common.settings.Settings;
3938
import org.elasticsearch.common.unit.ByteSizeValue;
39+
import org.elasticsearch.common.unit.TimeValue;
4040
import org.elasticsearch.http.HttpChannel;
4141
import org.elasticsearch.http.HttpHandlingSettings;
42+
import org.elasticsearch.http.HttpReadTimeoutException;
4243
import org.elasticsearch.http.HttpRequest;
4344
import org.elasticsearch.http.HttpResponse;
4445
import org.elasticsearch.http.HttpTransportSettings;
@@ -48,6 +49,7 @@
4849
import org.elasticsearch.nio.FlushOperation;
4950
import org.elasticsearch.nio.InboundChannelBuffer;
5051
import org.elasticsearch.nio.SocketChannelContext;
52+
import org.elasticsearch.nio.TaskScheduler;
5153
import org.elasticsearch.rest.RestRequest;
5254
import org.elasticsearch.rest.RestStatus;
5355
import org.elasticsearch.test.ESTestCase;
@@ -56,26 +58,23 @@
5658

5759
import java.io.IOException;
5860
import java.nio.ByteBuffer;
61+
import java.util.Arrays;
62+
import java.util.Iterator;
5963
import java.util.List;
6064
import java.util.function.BiConsumer;
6165

6266
import static org.elasticsearch.http.HttpTransportSettings.SETTING_CORS_ALLOW_CREDENTIALS;
6367
import static org.elasticsearch.http.HttpTransportSettings.SETTING_CORS_ALLOW_METHODS;
6468
import static org.elasticsearch.http.HttpTransportSettings.SETTING_CORS_ALLOW_ORIGIN;
6569
import static org.elasticsearch.http.HttpTransportSettings.SETTING_CORS_ENABLED;
66-
import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_COMPRESSION;
67-
import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_COMPRESSION_LEVEL;
68-
import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_DETAILED_ERRORS_ENABLED;
69-
import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_MAX_CHUNK_SIZE;
70-
import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_MAX_HEADER_SIZE;
71-
import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_MAX_INITIAL_LINE_LENGTH;
72-
import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_RESET_COOKIES;
73-
import static org.elasticsearch.http.HttpTransportSettings.SETTING_PIPELINING_MAX_EVENTS;
70+
import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_MAX_CONTENT_LENGTH;
71+
import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_READ_TIMEOUT;
7472
import static org.hamcrest.Matchers.equalTo;
7573
import static org.hamcrest.Matchers.is;
7674
import static org.hamcrest.Matchers.notNullValue;
7775
import static org.hamcrest.Matchers.nullValue;
7876
import static org.mockito.Matchers.any;
77+
import static org.mockito.Matchers.eq;
7978
import static org.mockito.Mockito.atLeastOnce;
8079
import static org.mockito.Mockito.mock;
8180
import static org.mockito.Mockito.times;
@@ -84,31 +83,24 @@
8483
public class HttpReadWriteHandlerTests extends ESTestCase {
8584

8685
private HttpReadWriteHandler handler;
87-
private NioHttpChannel nioHttpChannel;
86+
private NioHttpChannel channel;
8887
private NioHttpServerTransport transport;
88+
private TaskScheduler taskScheduler;
8989

9090
private final RequestEncoder requestEncoder = new RequestEncoder();
9191
private final ResponseDecoder responseDecoder = new ResponseDecoder();
9292

9393
@Before
9494
public void setMocks() {
9595
transport = mock(NioHttpServerTransport.class);
96-
Settings settings = Settings.EMPTY;
97-
ByteSizeValue maxChunkSize = SETTING_HTTP_MAX_CHUNK_SIZE.getDefault(settings);
98-
ByteSizeValue maxHeaderSize = SETTING_HTTP_MAX_HEADER_SIZE.getDefault(settings);
99-
ByteSizeValue maxInitialLineLength = SETTING_HTTP_MAX_INITIAL_LINE_LENGTH.getDefault(settings);
100-
HttpHandlingSettings httpHandlingSettings = new HttpHandlingSettings(1024,
101-
Math.toIntExact(maxChunkSize.getBytes()),
102-
Math.toIntExact(maxHeaderSize.getBytes()),
103-
Math.toIntExact(maxInitialLineLength.getBytes()),
104-
SETTING_HTTP_RESET_COOKIES.getDefault(settings),
105-
SETTING_HTTP_COMPRESSION.getDefault(settings),
106-
SETTING_HTTP_COMPRESSION_LEVEL.getDefault(settings),
107-
SETTING_HTTP_DETAILED_ERRORS_ENABLED.getDefault(settings),
108-
SETTING_PIPELINING_MAX_EVENTS.getDefault(settings),
109-
SETTING_CORS_ENABLED.getDefault(settings));
110-
nioHttpChannel = mock(NioHttpChannel.class);
111-
handler = new HttpReadWriteHandler(nioHttpChannel, transport, httpHandlingSettings, NioCorsConfigBuilder.forAnyOrigin().build());
96+
Settings settings = Settings.builder().put(SETTING_HTTP_MAX_CONTENT_LENGTH.getKey(), new ByteSizeValue(1024)).build();
97+
HttpHandlingSettings httpHandlingSettings = HttpHandlingSettings.fromSettings(settings);
98+
channel = mock(NioHttpChannel.class);
99+
taskScheduler = mock(TaskScheduler.class);
100+
101+
NioCorsConfig corsConfig = NioCorsConfigBuilder.forAnyOrigin().build();
102+
handler = new HttpReadWriteHandler(channel, transport, httpHandlingSettings, corsConfig, taskScheduler, System::nanoTime);
103+
handler.channelRegistered();
112104
}
113105

114106
public void testSuccessfulDecodeHttpRequest() throws IOException {
@@ -188,7 +180,7 @@ public void testDecodeHttpRequestContentLengthToLongGeneratesOutboundMessage() t
188180
flushOperation.getListener().accept(null, null);
189181
// Since we have keep-alive set to false, we should close the channel after the response has been
190182
// flushed
191-
verify(nioHttpChannel).close();
183+
verify(channel).close();
192184
} finally {
193185
response.release();
194186
}
@@ -335,10 +327,59 @@ public void testThatAnyOriginWorks() throws IOException {
335327
}
336328
}
337329

338-
private FullHttpResponse executeCorsRequest(final Settings settings, final String originValue, final String host) throws IOException {
330+
@SuppressWarnings("unchecked")
331+
public void testReadTimeout() throws IOException {
332+
TimeValue timeValue = TimeValue.timeValueMillis(500);
333+
Settings settings = Settings.builder().put(SETTING_HTTP_READ_TIMEOUT.getKey(), timeValue).build();
339334
HttpHandlingSettings httpHandlingSettings = HttpHandlingSettings.fromSettings(settings);
340-
NioCorsConfig nioCorsConfig = NioHttpServerTransport.buildCorsConfig(settings);
341-
HttpReadWriteHandler handler = new HttpReadWriteHandler(nioHttpChannel, transport, httpHandlingSettings, nioCorsConfig);
335+
DefaultFullHttpRequest nettyRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
336+
NioHttpRequest nioHttpRequest = new NioHttpRequest(nettyRequest, 0);
337+
NioHttpResponse httpResponse = nioHttpRequest.createResponse(RestStatus.OK, BytesArray.EMPTY);
338+
httpResponse.addHeader(HttpHeaderNames.CONTENT_LENGTH.toString(), "0");
339+
340+
NioCorsConfig corsConfig = NioCorsConfigBuilder.forAnyOrigin().build();
341+
TaskScheduler taskScheduler = new TaskScheduler();
342+
343+
Iterator<Integer> timeValues = Arrays.asList(0, 2, 4, 6, 8).iterator();
344+
handler = new HttpReadWriteHandler(channel, transport, httpHandlingSettings, corsConfig, taskScheduler, timeValues::next);
345+
handler.channelRegistered();
346+
347+
prepareHandlerForResponse(handler);
348+
SocketChannelContext context = mock(SocketChannelContext.class);
349+
HttpWriteOperation writeOperation = new HttpWriteOperation(context, httpResponse, mock(BiConsumer.class));
350+
handler.writeToBytes(writeOperation);
351+
352+
taskScheduler.pollTask(timeValue.getNanos() + 1).run();
353+
// There was a read. Do not close.
354+
verify(transport, times(0)).onException(eq(channel), any(HttpReadTimeoutException.class));
355+
356+
prepareHandlerForResponse(handler);
357+
prepareHandlerForResponse(handler);
358+
359+
taskScheduler.pollTask(timeValue.getNanos() + 3).run();
360+
// There was a read. Do not close.
361+
verify(transport, times(0)).onException(eq(channel), any(HttpReadTimeoutException.class));
362+
363+
handler.writeToBytes(writeOperation);
364+
365+
taskScheduler.pollTask(timeValue.getNanos() + 5).run();
366+
// There has not been a read, however there is still an inflight request. Do not close.
367+
verify(transport, times(0)).onException(eq(channel), any(HttpReadTimeoutException.class));
368+
369+
handler.writeToBytes(writeOperation);
370+
371+
taskScheduler.pollTask(timeValue.getNanos() + 7).run();
372+
// No reads and no inflight requests, close
373+
verify(transport, times(1)).onException(eq(channel), any(HttpReadTimeoutException.class));
374+
assertNull(taskScheduler.pollTask(timeValue.getNanos() + 9));
375+
}
376+
377+
private FullHttpResponse executeCorsRequest(final Settings settings, final String originValue, final String host) throws IOException {
378+
HttpHandlingSettings httpSettings = HttpHandlingSettings.fromSettings(settings);
379+
NioCorsConfig corsConfig = NioHttpServerTransport.buildCorsConfig(settings);
380+
HttpReadWriteHandler handler = new HttpReadWriteHandler(channel, transport, httpSettings, corsConfig, taskScheduler,
381+
System::nanoTime);
382+
handler.channelRegistered();
342383
prepareHandlerForResponse(handler);
343384
DefaultFullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
344385
if (originValue != null) {
@@ -360,7 +401,7 @@ private FullHttpResponse executeCorsRequest(final Settings settings, final Strin
360401

361402

362403

363-
private NioHttpRequest prepareHandlerForResponse(HttpReadWriteHandler handler) throws IOException {
404+
private void prepareHandlerForResponse(HttpReadWriteHandler handler) throws IOException {
364405
HttpMethod method = randomBoolean() ? HttpMethod.GET : HttpMethod.HEAD;
365406
HttpVersion version = randomBoolean() ? HttpVersion.HTTP_1_0 : HttpVersion.HTTP_1_1;
366407
String uri = "http://localhost:9090/" + randomAlphaOfLength(8);
@@ -385,7 +426,6 @@ private NioHttpRequest prepareHandlerForResponse(HttpReadWriteHandler handler) t
385426
assertEquals(HttpRequest.HttpVersion.HTTP_1_0, nioHttpRequest.protocolVersion());
386427
}
387428
assertEquals(nioHttpRequest.uri(), uri);
388-
return nioHttpRequest;
389429
}
390430

391431
private InboundChannelBuffer toChannelBuffer(ByteBuf buf) {

0 commit comments

Comments
 (0)