Skip to content

Fix abandoned connections on CONNECT ICAP handler#2394

Open
Le-onardo wants to merge 1 commit intosquid-cache:masterfrom
Le-onardo:master
Open

Fix abandoned connections on CONNECT ICAP handler#2394
Le-onardo wants to merge 1 commit intosquid-cache:masterfrom
Le-onardo:master

Conversation

@Le-onardo
Copy link

@Le-onardo Le-onardo commented Mar 24, 2026

When an ICAP REQMOD service returns a replacement reply (block page)
for a CONNECT request, handleAdaptedHeader() in
client_side_request.cc enters request_satisfaction_mode and sends
the reply directly through the HTTP stream chain. Since the CONNECT
was intercepted before reaching processRequest() in
client_side_request.cc where tunnelStart() would be called, there
is no tunnel and the block page is delivered as a normal HTTP
response.

CONNECT sets readMore to false in client_side.cc (expecting tunnel
takeover) and proxyKeepalive to true in client_side.cc (HTTP/1.1
default). Since request_satisfaction_mode bypasses the normal error
handling path where proxyKeepalive would be corrected, it is never
set to false. Therefore writeComplete() in Stream.cc skips the
closing of the connection and kick() in client_side.cc subsequently
finds an empty pipeline with readMore still set to false, abandoning
the connection with no read handler, no timeout, and no close for
up to 24 hours (client_lifetime).

A blocked CONNECT has no tunnel, so the connection should close
after delivering the reply. Set proxyKeepalive to false for CONNECT
requests entering request_satisfaction_mode in
handleAdaptedHeader(), so writeComplete() in Stream.cc closes the
connection after the block page is sent.

To reproduce: configure an ICAP REQMOD service that returns a
replacement reply (such as a block page) for CONNECT requests, then
send a CONNECT to a blocked destination. The ICAP server returns a
block page via REQMOD reply. Without the fix, the connection remains
open after the block page is delivered, abandoning a connection.
The fix introduces a way to close the connection after delivering
the block page.

@squid-anubis squid-anubis added the M-failed-description https://github.com/measurement-factory/anubis#pull-request-labels label Mar 24, 2026
@squid-anubis

This comment was marked as resolved.

@Le-onardo Le-onardo changed the title Bugfix: Fix abandoned connections on CONNECT in ICAP request_satisfac… Bugfix: Fix abandoned connections on CONNECT in ICAP request_satisfaction_mode Mar 24, 2026
@squid-anubis

This comment was marked as resolved.

@Le-onardo Le-onardo changed the title Bugfix: Fix abandoned connections on CONNECT in ICAP request_satisfaction_mode Fix abandoned connections on CONNECT in ICAP request_satisfaction_mode Mar 24, 2026
@squid-anubis

This comment was marked as resolved.

@Le-onardo Le-onardo changed the title Fix abandoned connections on CONNECT in ICAP request_satisfaction_mode Fix abandoned connections on CONNECT ICAP handler Mar 24, 2026
@squid-anubis

This comment was marked as resolved.

@squid-anubis squid-anubis removed the M-failed-description https://github.com/measurement-factory/anubis#pull-request-labels label Mar 24, 2026
Copy link
Contributor

@rousskov rousskov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for working on this bug fix and detailing your understanding of the related code logic.

Just to avoid a misunderstanding: None of my specific review questions and concerns are an indication that Squid functions correctly or that no fix is needed here.

request->flags.proxyKeepalive = false;
}

storeEntry()->replaceHttpReply(new_rep);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description: When an ICAP REQMOD service returns a replacement reply (block page) for a CONNECT request

In your environment,

  1. Was the CONNECT request received from a client (as opposed to faked by Squid)?
  2. Does the client open a TCP connection to Squid (as opposed to Squid intercepting a client connection)?

For example, if you are dealing with a real CONNECT that the client sends to Squid after opening a TCP connection to http_port, then the answers are "yes" and "yes".

  1. When the bug you are fixing manifests itself, do you get a level-1 WARNING: kick..abandoning... message in cache.log?

Squid needs to correctly handle all answer combinations, but knowing your environment(s) may help me guide this PR towards a proper long-term fix of the underlying problem...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 and 2 are a yes. It is a real connection to Squid from a client.

As to 3, this specific log line in client_side.cc (952-955) gets triggered if my described case is manifesting, which would be DBG_IMPORTANT

} else {
        // XXX: Can this happen? CONNECT tunnels have deferredRequest set.
        debugs(33, DBG_IMPORTANT, MYNAME << "abandoning " << clientConnection);
    }

As the comment indicates this is a path that would be unexpected to even be hit, but it is, as the connection is kept alive via the proxyKeepalive flag and flags.readMore being false.

request_satisfaction_mode = true;
request_satisfaction_offset = 0;

// make sure that CONNECT will be closed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect that we do not want to force the client-Squid connection to be closed after serving this adaptation-generated response, regardless of the request method. I assume that closing the connection makes the bug you observe go away, but that improvement alone is not sufficient to justify adding this special behavior. Most likely, we need more/different changes to properly address the underlying bug.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the connection is not closed it lingers as an open file descriptor and is not re-used anywhere. Rationally speaking I would expect that the connection would need to be closed at some point. Whether it is in the handleAdaptedHeader function or somewhere later. It seems as if there is a confusion between when a CONNECT tunnel is set and actually a HTTP response is set via the adaption generated response; if the latter case is hit, the tunnel should probably be not regarded as such but rather like a regular HTTP connection, thus going through the Stream handler, where the connection is closed "properly".

If this fix in he handleAdaptedHeader is not sufficient, I hope it works as an indication of an underlying issue.

@rousskov rousskov added the S-waiting-for-author author action is expected (and usually required) label Mar 24, 2026
@rousskov rousskov self-requested a review March 24, 2026 20:07
@rousskov rousskov added S-waiting-for-reviewer ready for review: Set this when requesting a (re)review using GitHub PR Reviewers box and removed S-waiting-for-author author action is expected (and usually required) labels Mar 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-for-reviewer ready for review: Set this when requesting a (re)review using GitHub PR Reviewers box

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants