conncontrol: reuse handling

Add protocol handler flag `PROTOPT_CONN_REUSE` to indicate that the
protocol allows reusing connections for other tranfers. Add that
to all handlers that support it.

Create connections with `conn->bits.close = FALSE` and remove all
the `connkeep()` calls in protocol handlers setup/connect implementations.
`PROTOPT_CONN_REUSE` assures that the default behaviour applies
at the end of a transfer without need to juggle the close bit.

`conn->bits.close` now serves as an additional indication that a
connection cannot be reused. Only protocol handles that allow
reuse need to set it to override the default behaviour.

Remove all `connclose()` and `connkeep()` calls from connection
filters. Filters should not modify connection flags. They are
supposed to run in eyeballing situations where a filter is just
one of many determining the outcome.

Fix http response header handling to only honour `Connection: close`
for HTTP/1.x versions.

Closes #19333
This commit is contained in:
Stefan Eissing 2025-11-03 13:12:50 +01:00 committed by Daniel Stenberg
parent a9e7a027ed
commit feea968512
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
21 changed files with 93 additions and 114 deletions

View file

@ -547,6 +547,47 @@ struct multi_done_ctx {
BIT(premature);
};
static bool multi_conn_should_close(struct connectdata *conn,
struct Curl_easy *data,
bool premature)
{
/* if conn->bits.close is TRUE, it means that the connection should be
closed in spite of everything else. */
if(conn->bits.close)
return TRUE;
/* if data->set.reuse_forbid is TRUE, it means the libcurl client has
forced us to close this connection. This is ignored for requests taking
place in a NTLM/NEGOTIATE authentication handshake. */
if(data->set.reuse_forbid
#ifdef USE_NTLM
&& !(conn->http_ntlm_state == NTLMSTATE_TYPE2 ||
conn->proxy_ntlm_state == NTLMSTATE_TYPE2)
#endif
#ifdef USE_SPNEGO
&& !(conn->http_negotiate_state == GSS_AUTHRECV ||
conn->proxy_negotiate_state == GSS_AUTHRECV)
#endif
)
return TRUE;
/* Unless this connection is for a "connect-only" transfer, it
* needs to be closed if the protocol handler does not support reuse. */
if(!data->set.connect_only && conn->handler &&
!(conn->handler->flags & PROTOPT_CONN_REUSE))
return TRUE;
/* if premature is TRUE, it means this connection was said to be DONE before
the entire request operation is complete and thus we cannot know in what
state it is for reusing, so we are forced to close it. In a perfect world
we can add code that keep track of if we really must close it here or not,
but currently we have no such detail knowledge. */
if(premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))
return TRUE;
return FALSE;
}
static void multi_done_locked(struct connectdata *conn,
struct Curl_easy *data,
void *userdata)
@ -587,32 +628,7 @@ static void multi_done_locked(struct connectdata *conn,
Curl_resolv_unlink(data, &data->state.dns[1]);
Curl_dnscache_prune(data);
/* if data->set.reuse_forbid is TRUE, it means the libcurl client has
forced us to close this connection. This is ignored for requests taking
place in a NTLM/NEGOTIATE authentication handshake
if conn->bits.close is TRUE, it means that the connection should be
closed in spite of all our efforts to be nice, due to protocol
restrictions in our or the server's end
if premature is TRUE, it means this connection was said to be DONE before
the entire request operation is complete and thus we cannot know in what
state it is for reusing, so we are forced to close it. In a perfect world
we can add code that keep track of if we really must close it here or not,
but currently we have no such detail knowledge.
*/
if((data->set.reuse_forbid
#ifdef USE_NTLM
&& !(conn->http_ntlm_state == NTLMSTATE_TYPE2 ||
conn->proxy_ntlm_state == NTLMSTATE_TYPE2)
#endif
#ifdef USE_SPNEGO
&& !(conn->http_negotiate_state == GSS_AUTHRECV ||
conn->proxy_negotiate_state == GSS_AUTHRECV)
#endif
) || conn->bits.close
|| (mdctx->premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) {
if(multi_conn_should_close(conn, data, mdctx->premature)) {
#ifndef CURL_DISABLE_VERBOSE_STRINGS
CURL_TRC_M(data, "multi_done, terminating conn #%" FMT_OFF_T " to %s:%d, "
"forbid=%d, close=%d, premature=%d, conn_multiplex=%d",
@ -2127,8 +2143,6 @@ static CURLMcode state_do(struct Curl_easy *data,
}
if(data->set.connect_only && !data->set.connect_only_ws) {
/* keep connection open for application to use the socket */
connkeep(data->conn, "CONNECT_ONLY");
multistate(data, MSTATE_DONE);
rc = CURLM_CALL_MULTI_PERFORM;
}