Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CURL_PUSH_ERROROUT: allow the push callback to fail the parent stream #5636

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions docs/TODO
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
1.17 Add support for IRIs
1.18 try next proxy if one doesn't work
1.20 SRV and URI DNS records
1.21 Add return code to CURLMOPT_PUSHFUNCTION to fail the connection
1.22 CURLINFO_PAUSE_STATE
1.23 Offer API to flush the connection pool
1.24 TCP Fast Open for windows
Expand Down Expand Up @@ -358,13 +357,6 @@
Offer support for resolving SRV and URI DNS records for libcurl to know which
server to connect to for various protocols (including HTTP!).

1.21 Add return code to CURLMOPT_PUSHFUNCTION to fail the connection

Allow the callback to return a value that would stop the entire operation,
like it can be done from most other callbacks.

See https://curl.haxx.se/mail/lib-2020-06/0099.html

1.22 CURLINFO_PAUSE_STATE

Return information about the transfer's current pause state, in both
Expand Down
3 changes: 3 additions & 0 deletions docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ the ownership of the CURL handle has been taken over by the application.
.IP "CURL_PUSH_DENY (1)"
The callback denies the stream and no data for this will reach the
application, the easy handle will be destroyed by libcurl.
.IP "CURL_PUSH_ERROROUT (2)"
Returning this will reject the pushed stream and return an error back on the
parent stream making it get closed with an error. (Added in curl 7.72.0)
.IP *
All other return codes are reserved for future use.
.SH DEFAULT
Expand Down
1 change: 1 addition & 0 deletions docs/libcurl/symbols-in-versions
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,7 @@ CURL_PROGRESSFUNC_CONTINUE 7.68.0
CURL_PROGRESS_BAR 7.1.1 - 7.4.1
CURL_PROGRESS_STATS 7.1.1 - 7.4.1
CURL_PUSH_DENY 7.44.0
CURL_PUSH_ERROROUT 7.72.0
CURL_PUSH_OK 7.44.0
CURL_READFUNC_ABORT 7.12.1
CURL_READFUNC_PAUSE 7.18.0
Expand Down
10 changes: 6 additions & 4 deletions include/curl/multi.h
Original file line number Diff line number Diff line change
Expand Up @@ -427,12 +427,14 @@ CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle,
* Name: curl_push_callback
*
* Desc: This callback gets called when a new stream is being pushed by the
* server. It approves or denies the new stream.
* server. It approves or denies the new stream. It can also decide
* to completely fail the connection.
*
* Returns: CURL_PUSH_OK or CURL_PUSH_DENY.
* Returns: CURL_PUSH_OK, CURL_PUSH_DENY or CURL_PUSH_ERROROUT
*/
#define CURL_PUSH_OK 0
#define CURL_PUSH_DENY 1
#define CURL_PUSH_OK 0
#define CURL_PUSH_DENY 1
#define CURL_PUSH_ERROROUT 2 /* added in 7.72.0 */

struct curl_pushheaders; /* forward declaration only */

Expand Down
27 changes: 18 additions & 9 deletions lib/http2.c
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ static int push_promise(struct Curl_easy *data,
struct connectdata *conn,
const nghttp2_push_promise *frame)
{
int rv;
int rv; /* one of the CURL_PUSH_* defines */
H2BUGF(infof(data, "PUSH_PROMISE received, stream %u!\n",
frame->promised_stream_id));
if(data->multi->push_cb) {
Expand All @@ -528,7 +528,7 @@ static int push_promise(struct Curl_easy *data,
struct Curl_easy *newhandle = duphandle(data);
if(!newhandle) {
infof(data, "failed to duplicate handle\n");
rv = 1; /* FAIL HARD */
rv = CURL_PUSH_DENY; /* FAIL HARD */
goto fail;
}

Expand All @@ -541,13 +541,15 @@ static int push_promise(struct Curl_easy *data,
if(!stream) {
failf(data, "Internal NULL stream!\n");
(void)Curl_close(&newhandle);
rv = 1;
rv = CURL_PUSH_DENY;
goto fail;
}

rv = set_transfer_url(newhandle, &heads);
if(rv)
if(rv) {
rv = CURL_PUSH_DENY;
goto fail;
}

Curl_set_in_callback(data, true);
rv = data->multi->push_cb(data, newhandle,
Expand All @@ -563,6 +565,7 @@ static int push_promise(struct Curl_easy *data,
stream->push_headers_used = 0;

if(rv) {
DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
/* denied, kill off the new handle again */
http2_stream_free(newhandle->req.protop);
newhandle->req.protop = NULL;
Expand All @@ -583,7 +586,7 @@ static int push_promise(struct Curl_easy *data,
http2_stream_free(newhandle->req.protop);
newhandle->req.protop = NULL;
Curl_close(&newhandle);
rv = 1;
rv = CURL_PUSH_DENY;
goto fail;
}

Expand All @@ -595,12 +598,13 @@ static int push_promise(struct Curl_easy *data,
infof(data, "failed to set user_data for stream %d\n",
frame->promised_stream_id);
DEBUGASSERT(0);
rv = CURL_PUSH_DENY;
goto fail;
}
}
else {
H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
rv = 1;
rv = CURL_PUSH_DENY;
}
fail:
return rv;
Expand Down Expand Up @@ -737,11 +741,16 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
case NGHTTP2_PUSH_PROMISE:
rv = push_promise(data_s, conn, &frame->push_promise);
if(rv) { /* deny! */
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
int h2;
DEBUGASSERT((rv > CURL_PUSH_OK) && (rv < CURL_PUSH_ERROROUT));
bagder marked this conversation as resolved.
Show resolved Hide resolved
h2 = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->push_promise.promised_stream_id,
NGHTTP2_CANCEL);
if(nghttp2_is_fatal(rv)) {
return rv;
if(nghttp2_is_fatal(h2))
return NGHTTP2_ERR_CALLBACK_FAILURE;
else if(rv == CURL_PUSH_ERROROUT) {
DEBUGF(infof(data_s, "Fail the parent stream (too)\n"));
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
break;
Expand Down