From a43cfa220437d70c653ffd87b6b5a857219f770f Mon Sep 17 00:00:00 2001 From: Ikhun Um Date: Thu, 9 Feb 2023 11:27:14 +0900 Subject: [PATCH] Return "431 Request Header Fields Too Large" for long headers on HTTP/1 (#4655) * Return "431 Request Header Fields Too Large" for long headers on HTTP/1 Motivation: Netty HTTP/1 codec raises `TooLongHttpHeaderException` if headers exceed the max length limit. However, `Http1RequestDecoder` does not take account into `TooLongHttpLineException` and returns "400 Bad Request" instead. Modifications: - Make `Http1RequestDecoder` return `431 Request Header Fields Too Large` if a Netty HttpRequest fails with `TooLongHttpHeaderException` Result: - "431 Request Header Fields Too Large" is now returned instead of `400 Bad Request` if the size of header exceeds `ServerBuilder.http1MaxHeaderSize()`. - Fixes #4609 * Fix the error message --- .../armeria/server/Http1RequestDecoder.java | 4 ++ .../armeria/server/LongHttp1HeadersTest.java | 54 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 core/src/test/java/com/linecorp/armeria/server/LongHttp1HeadersTest.java diff --git a/core/src/main/java/com/linecorp/armeria/server/Http1RequestDecoder.java b/core/src/main/java/com/linecorp/armeria/server/Http1RequestDecoder.java index 77461feadf6..7c8d0b337b8 100644 --- a/core/src/main/java/com/linecorp/armeria/server/Http1RequestDecoder.java +++ b/core/src/main/java/com/linecorp/armeria/server/Http1RequestDecoder.java @@ -59,6 +59,7 @@ import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.LastHttpContent; +import io.netty.handler.codec.http.TooLongHttpHeaderException; import io.netty.handler.codec.http.TooLongHttpLineException; import io.netty.handler.codec.http2.Http2CodecUtil; import io.netty.handler.codec.http2.Http2Error; @@ -151,6 +152,9 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception final Throwable cause = nettyReq.decoderResult().cause(); if (cause instanceof TooLongHttpLineException) { fail(id, null, HttpStatus.REQUEST_URI_TOO_LONG, "Too Long URI", cause); + } else if (cause instanceof TooLongHttpHeaderException) { + fail(id, null, HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE, + "Request Header Fields Too Large", cause); } else { fail(id, null, HttpStatus.BAD_REQUEST, "Decoder failure", cause); } diff --git a/core/src/test/java/com/linecorp/armeria/server/LongHttp1HeadersTest.java b/core/src/test/java/com/linecorp/armeria/server/LongHttp1HeadersTest.java new file mode 100644 index 00000000000..9e5b0032c58 --- /dev/null +++ b/core/src/test/java/com/linecorp/armeria/server/LongHttp1HeadersTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package com.linecorp.armeria.server; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import com.google.common.base.Strings; + +import com.linecorp.armeria.client.BlockingWebClient; +import com.linecorp.armeria.common.AggregatedHttpResponse; +import com.linecorp.armeria.common.HttpResponse; +import com.linecorp.armeria.common.HttpStatus; +import com.linecorp.armeria.common.SessionProtocol; +import com.linecorp.armeria.testing.junit5.server.ServerExtension; + +class LongHttp1HeadersTest { + + @RegisterExtension + static ServerExtension server = new ServerExtension() { + @Override + protected void configure(ServerBuilder sb) { + sb.http1MaxHeaderSize(100); + + sb.service("/", (ctx, req) -> HttpResponse.of("OK")); + } + }; + + @Test + void shouldReturn431WithLongHeadersForHttp1() { + final BlockingWebClient client = BlockingWebClient.of(server.uri(SessionProtocol.H1C)); + final AggregatedHttpResponse response = client.prepare() + .get("/") + .header("x-long", Strings.repeat("1", 100)) + .execute(); + assertThat(response.status()).isEqualTo(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE); + } +}