From b4ef4ece01b11245e7d0daee46a525a4b46d6e5c Mon Sep 17 00:00:00 2001 From: Jochen Delabie Date: Tue, 27 Oct 2026 11:36:55 +0100 Subject: [PATCH] Don't log web's Squid warm-up request as a failure Web warms up every new tunnel with GET http://127.0.0.1:1/squid-internal-tunnel-warmup through both Squid ports, to absorb the cold-start 502 before a customer's first request does. Port 1 is refused, so each answers 502 here, and under the default --log-http errors that printed two WARNING blocks with headers on every start, which read as a failed tunnel. HttpLogHandler now leaves /squid-internal requests out in errors mode, as TunnelProxyHandler already does for its own failure line. url and headers still log them, since those modes asked for every request. --- .../tunnel/proxy/HttpLogHandler.java | 15 ++++++++- .../tunnel/integration/HttpLoggingTest.java | 32 ++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/testingbot/tunnel/proxy/HttpLogHandler.java b/src/main/java/com/testingbot/tunnel/proxy/HttpLogHandler.java index c24ceaf..4085b30 100644 --- a/src/main/java/com/testingbot/tunnel/proxy/HttpLogHandler.java +++ b/src/main/java/com/testingbot/tunnel/proxy/HttpLogHandler.java @@ -124,7 +124,7 @@ private void log(Request request, Response response, String requestId, long star boolean failed = failure != null || status >= 500; // BODY is refused for this module, so it can only arrive here by mistake; treat it as // HEADERS rather than silently logging nothing. - if (mode == Mode.ERRORS && !failed) { + if (mode == Mode.ERRORS && (!failed || isSquidInternal(request))) { return; } @@ -149,6 +149,19 @@ private void log(Request request, Response response, String requestId, long star LOG.log(failed ? Level.WARNING : Level.INFO, line.toString()); } + /** + * Squid's own requests through the tunnel, which fail by design. Web warms up every new + * tunnel with {@code GET http://127.0.0.1:1/squid-internal-tunnel-warmup} through both of + * Squid's ports, to absorb the cold-start 502 before a customer's first request does. Port 1 + * is refused, so each answers 502 here, and printed a WARNING with headers on every start + * that read as a failed tunnel. Only the default {@code errors} mode drops them: {@code url} + * and {@code headers} asked for every request. + */ + private static boolean isSquidInternal(Request request) { + String path = request.getHttpURI().getPath(); + return path != null && path.startsWith("/squid-internal"); + } + /** CONNECT carries an authority rather than a URL; log whichever the request actually has. */ private static String target(Request request) { String authority = request.getHttpURI().getAuthority(); diff --git a/src/test/java/com/testingbot/tunnel/integration/HttpLoggingTest.java b/src/test/java/com/testingbot/tunnel/integration/HttpLoggingTest.java index b9d3a20..b6c611d 100644 --- a/src/test/java/com/testingbot/tunnel/integration/HttpLoggingTest.java +++ b/src/test/java/com/testingbot/tunnel/integration/HttpLoggingTest.java @@ -204,9 +204,13 @@ private String proxyGetEchoing(String extraHeaders) throws Exception { } private void proxyGet(String extraHeaders) throws Exception { + proxyGet("/page", extraHeaders); + } + + private void proxyGet(String path, String extraHeaders) throws Exception { try (Socket socket = new Socket("127.0.0.1", proxyPort)) { socket.setSoTimeout(10_000); - String request = "GET http://127.0.0.1:" + originPort + "/page HTTP/1.1\r\n" + String request = "GET http://127.0.0.1:" + originPort + path + " HTTP/1.1\r\n" + "Host: 127.0.0.1:" + originPort + "\r\n" + extraHeaders + "Connection: close\r\n\r\n"; @@ -295,6 +299,32 @@ void errors_logsWithHeadersOnServerError() throws Exception { assertThat(logged).contains("X-Custom: visible"); } + @Test + void errors_staysQuietForSquidsWarmupRequest() throws Exception { + // Web sends this through every new tunnel and it fails by design; logged, it printed + // two WARNINGs on every start that read as a broken tunnel. + startOrigin(502); + startProxy("errors", null); + + proxyGet("/squid-internal-tunnel-warmup", ""); + + // A deliberate wait, not a guess at readiness: this asserts a record never appears, + // so there is nothing to poll for and the only way to be wrong is to look too early. + Thread.sleep(200); + assertThat(captured.count()).isZero(); + } + + @Test + void url_stillLogsSquidsWarmupRequest() throws Exception { + // url asked for every request, so it is not this handler's place to leave one out. + startOrigin(502); + startProxy("url", null); + + proxyGet("/squid-internal-tunnel-warmup", ""); + + assertThat(awaitLogged()).contains("/squid-internal-tunnel-warmup").contains("502"); + } + @Test void reusesAnIncomingCorrelationId() throws Exception { // A trace that starts in the test framework should stay joined up through the tunnel.