Fix possible NPE when using HttpClientCodec (#9465)

Motivation:

It was possible to produce a NPE when we for examples received more responses as requests as we did not check if the queue did not contain a method before trying to compare method names.

Modifications:

- Add extra null check
- Add unit tet

Result:

Fixes https://github.com/netty/netty/issues/9459
This commit is contained in:
Norman Maurer 2019-08-16 08:17:18 +02:00
parent e4d9a17ad9
commit 96f92929ab
2 changed files with 64 additions and 37 deletions

View File

@ -160,7 +160,7 @@ public final class HttpClientCodec extends CombinedChannelDuplexHandler<HttpResp
return; return;
} }
if (msg instanceof HttpRequest && !done) { if (msg instanceof HttpRequest) {
queue.offer(((HttpRequest) msg).method()); queue.offer(((HttpRequest) msg).method());
} }
@ -233,6 +233,9 @@ public final class HttpClientCodec extends CombinedChannelDuplexHandler<HttpResp
// current response. // current response.
HttpMethod method = queue.poll(); HttpMethod method = queue.poll();
// If the remote peer did for example send multiple responses for one request (which is not allowed per
// spec but may still be possible) method will be null so guard against it.
if (method != null) {
char firstChar = method.name().charAt(0); char firstChar = method.name().charAt(0);
switch (firstChar) { switch (firstChar) {
case 'H': case 'H':
@ -261,8 +264,8 @@ public final class HttpClientCodec extends CombinedChannelDuplexHandler<HttpResp
// Successful CONNECT request results in a response with empty body. // Successful CONNECT request results in a response with empty body.
if (statusCode == 200) { if (statusCode == 200) {
if (HttpMethod.CONNECT.equals(method)) { if (HttpMethod.CONNECT.equals(method)) {
// Proxy connection established - Parse HTTP only if configured by parseHttpAfterConnectRequest, // Proxy connection established - Parse HTTP only if configured by
// else pass through. // parseHttpAfterConnectRequest, else pass through.
if (!parseHttpAfterConnectRequest) { if (!parseHttpAfterConnectRequest) {
done = true; done = true;
queue.clear(); queue.clear();
@ -272,7 +275,7 @@ public final class HttpClientCodec extends CombinedChannelDuplexHandler<HttpResp
} }
break; break;
} }
}
return super.isContentAlwaysEmpty(msg); return super.isContentAlwaysEmpty(msg);
} }

View File

@ -326,4 +326,28 @@ public class HttpClientCodecTest {
assertThat(ch.readInbound(), is(nullValue())); assertThat(ch.readInbound(), is(nullValue()));
} }
@Test
public void testMultipleResponses() {
String response = "HTTP/1.1 200 OK\r\n" +
"Content-Length: 0\r\n\r\n";
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
EmbeddedChannel ch = new EmbeddedChannel(codec, new HttpObjectAggregator(1024));
HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/");
assertTrue(ch.writeOutbound(request));
assertTrue(ch.writeInbound(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8)));
assertTrue(ch.writeInbound(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8)));
FullHttpResponse resp = ch.readInbound();
assertTrue(resp.decoderResult().isSuccess());
resp.release();
resp = ch.readInbound();
assertTrue(resp.decoderResult().isSuccess());
resp.release();
assertTrue(ch.finishAndReleaseAll());
}
} }