mirror of
https://github.com/curl/curl.git
synced 2026-08-26 16:13:32 +03:00
cfilters: fix event-based connection shutdown
- Adjust pollset during connection shutdown. - Separate the FIRSTSOCKET and SECONDSOCKET check so that one being in connect or shutdown no longer adds poll events for the other. - Fix shutdown state evaluation (as detailed below). - Add a unit test for Curl_conn_adjust_pollset. - Add a client for event testing. - Test that shutdown sockets stay with the socket callback until done. The pollset predicate now reads the connection's own shutdown state rather than going through data->conn, and the EXPIRE_SHUTDOWN arming in cshutdn_perform() works again: next_expire_ms started at 0 and could never be set, so a peer that never sends a close_notify would still park its connection forever, timeout or not. Reported-by: pszemus@users.noreply.github.com Fixes https://github.com/curl/curl/issues/22282 Closes https://github.com/curl/curl/pull/22304
This commit is contained in:
parent
33dc64fd0e
commit
820c014578
12 changed files with 612 additions and 21 deletions
|
|
@ -206,7 +206,7 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
|
|||
}
|
||||
|
||||
*done = FALSE;
|
||||
if(!Curl_shutdown_started(data, sockindex)) {
|
||||
if(!Curl_shutdown_started(data->conn, sockindex)) {
|
||||
Curl_shutdown_start(data, sockindex, 0);
|
||||
}
|
||||
else {
|
||||
|
|
@ -748,23 +748,29 @@ CURLcode Curl_conn_adjust_pollset(struct Curl_easy *data,
|
|||
struct easy_pollset *ps)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
bool want_io = !!ps->n;
|
||||
int i;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(conn);
|
||||
/* During connect time, connection filters may add sockets to the pollset
|
||||
* even when the transfer neither wants to send nor receive. And those
|
||||
* sockets, when having events, are served.
|
||||
* Once connected however, a transfer that neither wants to send nor receive
|
||||
* sockets, when having events, are served. The same applies to a
|
||||
* filter chain whose shutdown has started.
|
||||
* Once a filter chain is connected however and before its shutdown
|
||||
* starts, a transfer that neither wants to send nor receive
|
||||
* will never call the connection filters. Any sockets added by the filters
|
||||
* will not change state and POLLIN/POLLOUT events will trigger forever,
|
||||
* making us busy loop. See #21671 */
|
||||
if(ps->n || !Curl_conn_is_connected(conn, FIRSTSOCKET) ||
|
||||
(conn->cfilter[SECONDARYSOCKET] &&
|
||||
!Curl_conn_is_connected(conn, SECONDARYSOCKET))) {
|
||||
for(i = 0; (i < 2) && !result && conn; ++i) {
|
||||
* making us busy loop. See #21671.
|
||||
* Gate each filter chain on its own state, so that one chain being in
|
||||
* connect or shutdown does not add poll events for the other. Check
|
||||
* against the transfer's own interest, before any chain added sockets
|
||||
* of its own. */
|
||||
for(i = 0; (i < 2) && !result; ++i) {
|
||||
if(conn->cfilter[i] &&
|
||||
(want_io || !Curl_conn_is_connected(conn, i) ||
|
||||
Curl_shutdown_started(conn, i)))
|
||||
result = Curl_conn_cf_adjust_pollset(conn->cfilter[i], data, ps);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ UNITTEST timediff_t timeleft_now_ms(struct Curl_easy *data,
|
|||
timediff_t timeleft_ms = 0;
|
||||
timediff_t ctimeleft_ms = 0;
|
||||
|
||||
if(Curl_shutdown_started(data, FIRSTSOCKET))
|
||||
if(data->conn && Curl_shutdown_started(data->conn, FIRSTSOCKET))
|
||||
return Curl_shutdown_timeleft(data, data->conn, FIRSTSOCKET);
|
||||
else if(Curl_is_connecting(data)) {
|
||||
timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ?
|
||||
|
|
@ -164,13 +164,10 @@ void Curl_shutdown_clear(struct Curl_easy *data, int sockindex)
|
|||
memset(pt, 0, sizeof(*pt));
|
||||
}
|
||||
|
||||
bool Curl_shutdown_started(struct Curl_easy *data, int sockindex)
|
||||
bool Curl_shutdown_started(struct connectdata *conn, int sockindex)
|
||||
{
|
||||
if(data->conn) {
|
||||
struct curltime *pt = &data->conn->shutdown.start[sockindex];
|
||||
return (pt->tv_sec > 0) || (pt->tv_usec > 0);
|
||||
}
|
||||
return FALSE;
|
||||
const struct curltime *pt = &conn->shutdown.start[sockindex];
|
||||
return (pt->tv_sec > 0) || (pt->tv_usec > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ timediff_t Curl_conn_shutdown_timeleft(struct Curl_easy *data,
|
|||
void Curl_shutdown_clear(struct Curl_easy *data, int sockindex);
|
||||
|
||||
/* TRUE iff shutdown has been started */
|
||||
bool Curl_shutdown_started(struct Curl_easy *data, int sockindex);
|
||||
bool Curl_shutdown_started(struct connectdata *conn, int sockindex);
|
||||
|
||||
/*
|
||||
* Used to extract socket and connectdata struct for the most recent
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ static void cshutdn_run_once(struct Curl_easy *data,
|
|||
/* We expect to be attached when called */
|
||||
DEBUGASSERT(data->conn == conn);
|
||||
|
||||
if(!Curl_shutdown_started(data, FIRSTSOCKET)) {
|
||||
if(!Curl_shutdown_started(conn, FIRSTSOCKET)) {
|
||||
Curl_shutdown_start(data, FIRSTSOCKET, 0);
|
||||
}
|
||||
|
||||
|
|
@ -253,7 +253,7 @@ static void cshutdn_perform(struct cshutdn *cshutdn,
|
|||
/* idata has one timer list, but maybe more than one connection.
|
||||
* Set EXPIRE_SHUTDOWN to the smallest time left for all. */
|
||||
ms = Curl_conn_shutdown_timeleft(data, conn);
|
||||
if(ms && ms < next_expire_ms)
|
||||
if(ms && (!next_expire_ms || (ms < next_expire_ms)))
|
||||
next_expire_ms = ms;
|
||||
}
|
||||
e = enext;
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ static bool xfer_recv_shutdown_started(struct Curl_easy *data)
|
|||
{
|
||||
if(!data || !data->conn)
|
||||
return FALSE;
|
||||
return Curl_shutdown_started(data, data->conn->recv_idx);
|
||||
return Curl_shutdown_started(data->conn, data->conn->recv_idx);
|
||||
}
|
||||
|
||||
CURLcode Curl_xfer_send_shutdown(struct Curl_easy *data, bool *done)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue