From 3e790503d61d6aa2161ff076dc8ce20bb808c041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Mon, 10 Aug 2026 20:12:42 +0200 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=85=20Add=20tests=20for=20correct=20h?= =?UTF-8?q?andling=20of=20Connection=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add remove the header from some requests where it didn’t matter. --- .../clients/http/ToadletContextImplTest.java | 51 +++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/test/freenet/clients/http/ToadletContextImplTest.java b/test/freenet/clients/http/ToadletContextImplTest.java index 9081f77403..c9efe2edb9 100644 --- a/test/freenet/clients/http/ToadletContextImplTest.java +++ b/test/freenet/clients/http/ToadletContextImplTest.java @@ -28,9 +28,10 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.any; @@ -189,14 +190,14 @@ public void sendingPostRequestWithoutContentLengthHeaderResultsInHttpStatus400() @Test public void sendingPostRequestWithInvalidContentLengthHeaderResultsInHttpStatus400() throws Exception { - sendRequest("POST /invalid-content-length HTTP/1.1\r\nConnection: Keep-Alive\r\nContent-Length: invalid\r\n\r\n", httpResponses -> { + sendRequest("POST /invalid-content-length HTTP/1.1\r\nContent-Length: invalid\r\n\r\n", httpResponses -> { assertThat(httpResponses, contains(hasStatus(equalTo(400), equalTo("Bad Request")))); }); } @Test public void sendingRequestAfterPostRequestWithInvalidContentLengthHeaderResultsInASingleResponseBeingSent() throws Exception { - sendRequest("POST /invalid-content-length HTTP/1.1\r\nConnection: Keep-Alive\r\nContent-Length: invalid\r\n\r\nGET / HTTP/1.0\r\n\r\n", httpResponses -> { + sendRequest("POST /invalid-content-length HTTP/1.1\r\nContent-Length: invalid\r\n\r\nGET / HTTP/1.0\r\n\r\n", httpResponses -> { assertThat(httpResponses, contains(hasStatus(equalTo(400), equalTo("Bad Request")))); }); } @@ -317,7 +318,7 @@ public void pipeliningAPutAndAGetRequestResultsInTwoSuccessfulRequests() throws postToadlet.allowPostWithoutPassword(); when(toadletContainer.findToadlet(new URI("/post-request"))).thenReturn(postToadlet); when(toadletContainer.findToadlet(new URI("/get-request"))).thenReturn(homepageToadlet); - sendRequest("POST /post-request HTTP/1.1\r\nContent-Length: 0\r\n\r\nGET /get-request HTTP/1.1\r\nConnection: close\r\n\r\n", httpResponses -> { + sendRequest("POST /post-request HTTP/1.1\r\nContent-Length: 0\r\n\r\nGET /get-request HTTP/1.1\r\n\r\n", httpResponses -> { assertThat(httpResponses, contains( allOf(hasStatus(200), hasBody(equalTo("POST OK\n".getBytes(UTF_8)))), allOf(hasStatus(200), hasBody(equalTo("GET OK\n".getBytes(UTF_8)))) @@ -325,6 +326,48 @@ public void pipeliningAPutAndAGetRequestResultsInTwoSuccessfulRequests() throws }); } + @Test + public void requestingConnectionCloseWillReplyWithConnectionCloseWhenPipeliningIsNotAllowed() throws Exception { + when(toadletContainer.findToadlet(URI.create("/requesting-connection-close"))).thenReturn(homepageToadlet); + sendRequest("GET /requesting-connection-close HTTP/1.1\r\nConnection: close\r\n\r\n", httpResponses -> { + assertThat(httpResponses, contains( + hasHeader("Connection", contains(equalToIgnoringCase("close"))) + )); + }); + } + + @Test + public void requestingConnectionKeepAliveWillReplyWithConnectionCloseWhenPipeliningIsNotAllowed() throws Exception { + when(toadletContainer.findToadlet(URI.create("/requesting-connection-keep-alive"))).thenReturn(homepageToadlet); + sendRequest("GET /requesting-connection-keep-alive HTTP/1.1\r\nConnection: keep-alive\r\n\r\n", httpResponses -> { + assertThat(httpResponses, contains( + hasHeader("Connection", contains(equalToIgnoringCase("close"))) + )); + }); + } + + @Test + public void requestingConnectionCloseWillReplyWithConnectionCloseWhenPipeliningIsAllowed() throws Exception { + when(toadletContainer.enablePersistentConnections()).thenReturn(true); + when(toadletContainer.findToadlet(URI.create("/requesting-connection-close"))).thenReturn(homepageToadlet); + sendRequest("GET /requesting-connection-close HTTP/1.1\r\nConnection: close\r\n\r\n", httpResponses -> { + assertThat(httpResponses, contains( + hasHeader("Connection", contains(equalToIgnoringCase("close"))) + )); + }); + } + + @Test + public void requestingConnectionKeepAliveWillReplyWithConnectionKeepAliveWhenPipeliningIsAllowed() throws Exception { + when(toadletContainer.enablePersistentConnections()).thenReturn(true); + when(toadletContainer.findToadlet(URI.create("/requesting-connection-keep-alive"))).thenReturn(homepageToadlet); + sendRequest("GET /requesting-connection-keep-alive HTTP/1.1\r\nConnection: keep-alive\r\n\r\n", httpResponses -> { + assertThat(httpResponses, contains( + hasHeader("Connection", contains(equalToIgnoringCase("keep-alive"))) + )); + }); + } + @Test public void ioExceptionInGetMethodOfToadletWillCauseHttpStatus500() throws Exception { when(toadletContainer.findToadlet(new URI("/error-in-get"))).thenReturn(errorToadlet); From 10fa0cf323ffef70acba03073d136316ed8c5cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Mon, 10 Aug 2026 20:12:47 +0200 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Remove=20empty=20line?= =?UTF-8?q?=20in=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/freenet/clients/http/ToadletContextImplTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/test/freenet/clients/http/ToadletContextImplTest.java b/test/freenet/clients/http/ToadletContextImplTest.java index c9efe2edb9..79d265fb15 100644 --- a/test/freenet/clients/http/ToadletContextImplTest.java +++ b/test/freenet/clients/http/ToadletContextImplTest.java @@ -56,7 +56,6 @@ public void requestingHomepageWillReturnHomepage() throws Exception { assertThat(httpResponses, contains(allOf( hasStatus(200), hasBody(equalTo("GET OK\n".getBytes(UTF_8))) ))); - }); } From 4387f1f6ec5face755bfca4663afbb13717224b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Mon, 10 Aug 2026 20:12:49 +0200 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9C=85=20Add=20one=20more=20test=20for?= =?UTF-8?q?=20correctly=20processing=20the=20Content-Length=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/freenet/clients/http/ToadletContextImplTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/freenet/clients/http/ToadletContextImplTest.java b/test/freenet/clients/http/ToadletContextImplTest.java index 79d265fb15..3199fa29d6 100644 --- a/test/freenet/clients/http/ToadletContextImplTest.java +++ b/test/freenet/clients/http/ToadletContextImplTest.java @@ -187,6 +187,13 @@ public void sendingPostRequestWithoutContentLengthHeaderResultsInHttpStatus400() }); } + @Test + public void sendingPostRequestWithNegativeContentLengthHeaderResultsInHttpStatus400() throws Exception { + sendRequest("POST /negative-content-length HTTP/1.0\r\nContent-Length: -123\r\n\r\n", httpResponses -> { + assertThat(httpResponses, contains(hasStatus(equalTo(400), equalTo("Bad Request")))); + }); + } + @Test public void sendingPostRequestWithInvalidContentLengthHeaderResultsInHttpStatus400() throws Exception { sendRequest("POST /invalid-content-length HTTP/1.1\r\nContent-Length: invalid\r\n\r\n", httpResponses -> {