Fix HttpContentEncoder does not handle multiple Accept-Encoding (#9557)
Motivation: At the current moment HttpContentEncoder handle only first value of multiple accept-encoding headers. Modification: Join multiple accept-encoding headers to one separated by comma. Result: Fixes #9553
This commit is contained in:
parent
4a60152dd8
commit
f26840478a
@ -24,11 +24,14 @@ import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.handler.codec.MessageToMessageCodec;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaderNames.*;
|
||||
|
||||
/**
|
||||
* Encodes the content of the outbound {@link HttpResponse} and {@link HttpContent}.
|
||||
* The original content is replaced with the new content encoded by the
|
||||
@ -73,21 +76,30 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpReque
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, HttpRequest msg, List<Object> out)
|
||||
throws Exception {
|
||||
CharSequence acceptedEncoding = msg.headers().get(HttpHeaderNames.ACCEPT_ENCODING);
|
||||
if (acceptedEncoding == null) {
|
||||
acceptedEncoding = HttpContentDecoder.IDENTITY;
|
||||
protected void decode(ChannelHandlerContext ctx, HttpRequest msg, List<Object> out) throws Exception {
|
||||
CharSequence acceptEncoding;
|
||||
List<String> acceptEncodingHeaders = msg.headers().getAll(ACCEPT_ENCODING);
|
||||
switch (acceptEncodingHeaders.size()) {
|
||||
case 0:
|
||||
acceptEncoding = HttpContentDecoder.IDENTITY;
|
||||
break;
|
||||
case 1:
|
||||
acceptEncoding = acceptEncodingHeaders.get(0);
|
||||
break;
|
||||
default:
|
||||
// Multiple message-header fields https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
||||
acceptEncoding = StringUtil.join(",", acceptEncodingHeaders);
|
||||
break;
|
||||
}
|
||||
|
||||
HttpMethod method = msg.method();
|
||||
if (HttpMethod.HEAD.equals(method)) {
|
||||
acceptedEncoding = ZERO_LENGTH_HEAD;
|
||||
acceptEncoding = ZERO_LENGTH_HEAD;
|
||||
} else if (HttpMethod.CONNECT.equals(method)) {
|
||||
acceptedEncoding = ZERO_LENGTH_CONNECT;
|
||||
acceptEncoding = ZERO_LENGTH_CONNECT;
|
||||
}
|
||||
|
||||
acceptEncodingQueue.add(acceptedEncoding);
|
||||
acceptEncodingQueue.add(acceptEncoding);
|
||||
out.add(ReferenceCountUtil.retain(msg));
|
||||
}
|
||||
|
||||
|
@ -500,6 +500,39 @@ public class HttpContentCompressorTest {
|
||||
assertTrue(ch.finishAndReleaseAll());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleAcceptEncodingHeaders() {
|
||||
FullHttpRequest request = newRequest();
|
||||
request.headers().set(HttpHeaderNames.ACCEPT_ENCODING, "unknown; q=1.0")
|
||||
.add(HttpHeaderNames.ACCEPT_ENCODING, "gzip; q=0.5")
|
||||
.add(HttpHeaderNames.ACCEPT_ENCODING, "deflate; q=0");
|
||||
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor());
|
||||
|
||||
assertTrue(ch.writeInbound(request));
|
||||
|
||||
FullHttpResponse res = new DefaultFullHttpResponse(
|
||||
HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
|
||||
Unpooled.copiedBuffer("Gzip Win", CharsetUtil.US_ASCII));
|
||||
assertTrue(ch.writeOutbound(res));
|
||||
|
||||
assertEncodedResponse(ch);
|
||||
HttpContent c = ch.readOutbound();
|
||||
assertThat(ByteBufUtil.hexDump(c.content()), is("1f8b080000000000000072afca2c5008cfcc03000000ffff"));
|
||||
c.release();
|
||||
|
||||
c = ch.readOutbound();
|
||||
assertThat(ByteBufUtil.hexDump(c.content()), is("03001f2ebf0f08000000"));
|
||||
c.release();
|
||||
|
||||
LastHttpContent last = ch.readOutbound();
|
||||
assertThat(last.content().readableBytes(), is(0));
|
||||
last.release();
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertTrue(ch.finishAndReleaseAll());
|
||||
}
|
||||
|
||||
private static FullHttpRequest newRequest() {
|
||||
FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
|
||||
req.headers().set(HttpHeaderNames.ACCEPT_ENCODING, "gzip");
|
||||
|
@ -17,6 +17,7 @@ package io.netty.util.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
@ -597,6 +598,36 @@ public final class StringUtil {
|
||||
return start == 0 && end == length - 1 ? value : value.subSequence(start, end + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a char sequence that contains all {@code elements} joined by a given separator.
|
||||
*
|
||||
* @param separator for each element
|
||||
* @param elements to join together
|
||||
*
|
||||
* @return a char sequence joined by a given separator.
|
||||
*/
|
||||
public static CharSequence join(CharSequence separator, Iterable<? extends CharSequence> elements) {
|
||||
ObjectUtil.checkNotNull(separator, "separator");
|
||||
ObjectUtil.checkNotNull(elements, "elements");
|
||||
|
||||
Iterator<? extends CharSequence> iterator = elements.iterator();
|
||||
if (!iterator.hasNext()) {
|
||||
return EMPTY_STRING;
|
||||
}
|
||||
|
||||
CharSequence firstElement = iterator.next();
|
||||
if (!iterator.hasNext()) {
|
||||
return firstElement;
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder(firstElement);
|
||||
do {
|
||||
builder.append(separator).append(iterator.next());
|
||||
} while (iterator.hasNext());
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code length} if no OWS is found.
|
||||
*/
|
||||
@ -622,4 +653,5 @@ public final class StringUtil {
|
||||
private static boolean isOws(char c) {
|
||||
return c == SPACE || c == TAB;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -534,4 +534,19 @@ public class StringUtilTest {
|
||||
assertEquals("", StringUtil.trimOws("\t ").toString());
|
||||
assertEquals("a b", StringUtil.trimOws("\ta b \t").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoin() {
|
||||
assertEquals("",
|
||||
StringUtil.join(",", Collections.<CharSequence>emptyList()).toString());
|
||||
assertEquals("a",
|
||||
StringUtil.join(",", Collections.singletonList("a")).toString());
|
||||
assertEquals("a,b",
|
||||
StringUtil.join(",", Arrays.asList("a", "b")).toString());
|
||||
assertEquals("a,b,c",
|
||||
StringUtil.join(",", Arrays.asList("a", "b", "c")).toString());
|
||||
assertEquals("a,b,c,null,d",
|
||||
StringUtil.join(",", Arrays.asList("a", "b", "c", null, "d")).toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user