diff --git a/docs/libcurl/curl_easy_pause.md b/docs/libcurl/curl_easy_pause.md index 4a37ed1c14..f0c9230da5 100644 --- a/docs/libcurl/curl_easy_pause.md +++ b/docs/libcurl/curl_easy_pause.md @@ -28,8 +28,8 @@ CURLcode curl_easy_pause(CURL *handle, int action); Using this function, you can explicitly mark a running connection to get paused, and you can unpause a connection that was previously paused. Unlike -most other libcurl functions, curl_easy_pause(3) can be used from within -callbacks. +most other libcurl functions, curl_easy_pause(3) can be used from within all +callbacks except the socket callback set with CURLMOPT_SOCKETFUNCTION(3). A connection can be paused by using this function or by letting the read or the write callbacks return the proper return code (*CURL_READFUNC_PAUSE* and diff --git a/lib/easy.c b/lib/easy.c index d60bdaed7b..2e2c4d0e25 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -1138,18 +1138,19 @@ void curl_easy_reset(CURL *curl) CURLcode curl_easy_pause(CURL *curl, int action) { CURLcode result = CURLE_OK; - bool recursive = FALSE; bool changed = FALSE; struct Curl_easy *data = curl; bool recv_paused, recv_paused_new; bool send_paused, send_paused_new; + uint8_t in_c; if(!GOOD_EASY_HANDLE(data) || !data->conn) /* crazy input, do not continue */ return CURLE_BAD_FUNCTION_ARGUMENT; - if(Curl_is_in_callback(data)) - recursive = TRUE; + in_c = Curl_is_in_callback(data); + if(in_c == IN_CALLBACK_FORBID_EASY_PAUSE) + return CURLE_RECURSIVE_API_CALL; recv_paused = Curl_xfer_recv_is_paused(data); recv_paused_new = (action & CURLPAUSE_RECV); @@ -1188,10 +1189,10 @@ CURLcode curl_easy_pause(CURL *curl, int action) if(Curl_multi_ev_assess_xfer(data->multi, data) && !result) result = CURLE_ABORTED_BY_CALLBACK; - if(recursive) + if(in_c) /* this might have called a callback recursively which might have set this to false again on exit */ - Curl_set_in_callback(data, TRUE); + Curl_set_in_callback(data, in_c); return result; } diff --git a/lib/multi.c b/lib/multi.c index d6ae111d8e..51b0917ce7 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -3906,15 +3906,16 @@ static void process_pending_handles(struct Curl_multi *multi) } } -void Curl_set_in_callback(struct Curl_easy *data, bool value) +/* 'value' used to be a boolean but can now also contain more info */ +void Curl_set_in_callback(struct Curl_easy *data, uint8_t value) { if(data && data->multi) data->multi->in_callback = value; } -bool Curl_is_in_callback(struct Curl_easy *data) +uint8_t Curl_is_in_callback(struct Curl_easy *data) { - return data && data->multi && data->multi->in_callback; + return (data && data->multi) ? data->multi->in_callback : IN_CALLBACK_NO; } unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi) diff --git a/lib/multi_ev.c b/lib/multi_ev.c index 71e22b9b19..12d1ca2abb 100644 --- a/lib/multi_ev.c +++ b/lib/multi_ev.c @@ -34,8 +34,7 @@ #include "uint-spbset.h" #include "multihandle.h" - -static void mev_in_callback(struct Curl_multi *multi, bool value) +static void mev_in_callback(struct Curl_multi *multi, uint8_t value) { multi->in_callback = value; } @@ -209,12 +208,10 @@ static CURLMcode mev_forget_socket(struct Curl_multi *multi, if(entry->announced && multi->socket_cb) { NOVERBOSE((void)cause); CURL_TRC_M(data, "ev %s, call(fd=%" FMT_SOCKET_T ", ev=REMOVE)", cause, s); - mev_in_callback(multi, TRUE); + mev_in_callback(multi, IN_CALLBACK_FORBID_EASY_PAUSE); rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, multi->socket_userp, entry->user_data); - mev_in_callback(multi, FALSE); - /* curl_easy_pause() is documented as callable from any callback; it - * re-enters mev_assess() which may free this 'entry'. Re-fetch. */ + mev_in_callback(multi, IN_CALLBACK_NO); entry = mev_sh_entry_get(&multi->ev.sh_entries, s); if(entry) entry->announced = FALSE; @@ -285,10 +282,10 @@ static CURLMcode mev_sh_entry_update(struct Curl_multi *multi, CURL_TRC_M(data, "ev update call(fd=%" FMT_SOCKET_T ", ev=%s%s)", s, (comboaction & CURL_POLL_IN) ? "IN" : "", (comboaction & CURL_POLL_OUT) ? "OUT" : ""); - mev_in_callback(multi, TRUE); + mev_in_callback(multi, IN_CALLBACK_FORBID_EASY_PAUSE); rc = multi->socket_cb(data, s, comboaction, multi->socket_userp, entry->user_data); - mev_in_callback(multi, FALSE); + mev_in_callback(multi, IN_CALLBACK_NO); if(rc == -1) { multi->dead = TRUE; return CURLM_ABORTED_BY_CALLBACK; diff --git a/lib/multihandle.h b/lib/multihandle.h index 19dd2ffcdf..5cf2c640e5 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -45,6 +45,10 @@ struct Curl_message { struct CURLMsg extmsg; }; +#define IN_CALLBACK_NO 0 +#define IN_CALLBACK_YES 1 +#define IN_CALLBACK_FORBID_EASY_PAUSE 2 + /* NOTE: if you add a state here, add the name to the statenames[] array * in curl_trc.c as well! */ @@ -179,10 +183,10 @@ struct Curl_multi { #endif uint32_t last_pending_mid; /* mid of last pending transfer rescheduled */ uint32_t last_resolv_id; /* id of the last DNS resolve operation */ + uint8_t in_callback; /* conditions for executing in callbacks */ BIT(ipv6_works); BIT(multiplexing); /* multiplexing wanted */ BIT(recheckstate); /* see Curl_multi_connchanged */ - BIT(in_callback); /* true while executing a callback */ BIT(in_ntfy_callback); /* true while dispatching notifications */ #ifdef USE_OPENSSL BIT(ssl_seeded); diff --git a/lib/multiif.h b/lib/multiif.h index 039db269e0..7fad9ca33e 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -37,8 +37,8 @@ void Curl_attach_connection(struct Curl_easy *data, struct connectdata *conn); void Curl_detach_connection(struct Curl_easy *data); bool Curl_multiplex_wanted(const struct Curl_multi *multi); -void Curl_set_in_callback(struct Curl_easy *data, bool value); -bool Curl_is_in_callback(struct Curl_easy *data); +void Curl_set_in_callback(struct Curl_easy *data, uint8_t value); +uint8_t Curl_is_in_callback(struct Curl_easy *data); CURLcode Curl_preconnect(struct Curl_easy *data); bool Curl_is_connecting(struct Curl_easy *data); diff --git a/tests/libtest/lib758.c b/tests/libtest/lib758.c index 2fa8ef3ab1..794e4cce06 100644 --- a/tests/libtest/lib758.c +++ b/tests/libtest/lib758.c @@ -147,6 +147,7 @@ static int t758_curlSocketCallback(CURL *curl, curl_socket_t s, int action, void *userp, void *socketp) { struct t758_ReadWriteSockets *sockets = userp; + CURLcode result; (void)curl; (void)socketp; @@ -158,6 +159,14 @@ static int t758_curlSocketCallback(CURL *curl, curl_socket_t s, int action, return -1; } + /* Pause is forbidden in this callback. This also returns + CURLE_BAD_FUNCTION_ARGUMENT before the connection has been setup. */ + result = curl_easy_pause(curl, CURLPAUSE_ALL); + if(!result) { + t758_msg("<- curl_easy_pause should return error!"); + return -1; + } + if(action == CURL_POLL_IN || action == CURL_POLL_INOUT) if(t758_addFd(&sockets->read, s, "read")) return -1; /* bail out */