From b3488d133ed34a823089438b799adc0dec66ed16 Mon Sep 17 00:00:00 2001 From: Renato Athaydes Date: Tue, 22 Aug 2023 21:14:05 +0200 Subject: [PATCH] Provide a request to the Router tunnel method. --- .../main/java/rawhttp/core/server/Router.java | 8 +++--- .../rawhttp/core/server/TcpRawHttpServer.java | 2 +- samples/build.gradle | 4 +++ .../rawhttp/samples/http-connect-sample.kt | 25 +++++++++++-------- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/rawhttp-core/src/main/java/rawhttp/core/server/Router.java b/rawhttp-core/src/main/java/rawhttp/core/server/Router.java index c62f6b9..45d6ad2 100644 --- a/rawhttp-core/src/main/java/rawhttp/core/server/Router.java +++ b/rawhttp-core/src/main/java/rawhttp/core/server/Router.java @@ -52,9 +52,8 @@ default Optional> continueResponse(RequestLine requestLine } /** - * Tunnel the client to the provided URI. + * Tunnel the client as asked by a CONNECT HTTP request. *

- * This method is called when a client requests to CONNECT to another location. * By default, the client is closed and an {@link UnsupportedOperationException} is thrown. *

* This method is called from a request Thread, so it's advisable that implementations that @@ -62,10 +61,11 @@ default Optional> continueResponse(RequestLine requestLine *

* See RFC-9110 Section 9.3.6. * - * @param client requesting tunneling. + * @param request the CONNECT request + * @param client requesting tunneling. * @throws IOException if an IO problem occurs */ - default void tunnel(Socket client) throws IOException { + default void tunnel(RawHttpRequest request, Socket client) throws IOException { client.close(); throw new UnsupportedOperationException("CONNECT request is not supported"); } diff --git a/rawhttp-core/src/main/java/rawhttp/core/server/TcpRawHttpServer.java b/rawhttp-core/src/main/java/rawhttp/core/server/TcpRawHttpServer.java index 2b8d6ec..9a67ca1 100644 --- a/rawhttp-core/src/main/java/rawhttp/core/server/TcpRawHttpServer.java +++ b/rawhttp-core/src/main/java/rawhttp/core/server/TcpRawHttpServer.java @@ -298,7 +298,7 @@ private void handle(Socket client, ServerSocket serverSocket) { } if (request.getMethod().equalsIgnoreCase("CONNECT") && response.getStartLine().isSuccess()) { - router.tunnel(client); + router.tunnel(request, client); break; // now it's between the client and the router } } catch (SocketTimeoutException e) { diff --git a/samples/build.gradle b/samples/build.gradle index 990e0d6..a2e7db0 100644 --- a/samples/build.gradle +++ b/samples/build.gradle @@ -7,6 +7,10 @@ test { enabled project.hasProperty('run-samples') } +compileTestKotlin { + kotlinOptions.jvmTarget = '1.8' +} + dependencies { implementation project(':rawhttp-core') implementation project(':rawhttp-httpcomponents') diff --git a/samples/src/test/kotlin/rawhttp/samples/http-connect-sample.kt b/samples/src/test/kotlin/rawhttp/samples/http-connect-sample.kt index e753487..93e11ea 100644 --- a/samples/src/test/kotlin/rawhttp/samples/http-connect-sample.kt +++ b/samples/src/test/kotlin/rawhttp/samples/http-connect-sample.kt @@ -36,15 +36,18 @@ object TunnelingRouter : Router { } // the server lets us know if we need to start a tunnel for the client - override fun tunnel(client: Socket) { - // in this example, we only read 32 bytes - val input = ByteArray(32) - client.getInputStream().read(input) - // revert the bytes and send it back - input.reverse() - client.getOutputStream().use { - it.write(input) - } + override fun tunnel(request: RawHttpRequest, client: Socket) { + // always free the request thread by handling the tunnel on another Thread + Thread { + // in this example, we only read 32 bytes + val input = ByteArray(32) + client.getInputStream().read(input) + // revert the bytes and send it back + input.reverse() + client.getOutputStream().use { + it.write(input) + } + }.start() } } @@ -92,7 +95,9 @@ class HttpConnectSample { clientSocket!!.getOutputStream().write((1..32).map { it.toByte() }.toByteArray()) // the server should reverse the message as that's what the Router implementation does - val tunnelResponse = clientSocket!!.getInputStream().readAllBytes() + val tunnelResponse = ByteArray(32).apply { + clientSocket!!.getInputStream().read(this) + } tunnelResponse.toList() shouldContainExactly (32 downTo 1).map { it.toByte() } }