summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2020-07-16 00:24:29 +0200
committerDaniel Stenberg <daniel@haxx.se>2020-07-16 00:24:29 +0200
commit954cd3eb482a00fda610ca64498d50db2b2e8298 (patch)
tree261374f3a3c9c158a42b0c3bfe33d5cb6bd18b78
parent2f5d0e497e65aefd655a08e5ca96c70cc3123b40 (diff)
downloadcurl-954cd3eb482a00fda610ca64498d50db2b2e8298.tar.gz
CURL_PUSH_ERROROUT: allow the push callback to fail the parent stream
... by adding support for a new dedicated return code. Suggested-by: Jonathan Cardoso Assisted-by: Erik Johansson URL: https://curl.haxx.se/mail/lib-2020-06/0099.html Closes #5636
-rw-r--r--docs/TODO8
-rw-r--r--docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.33
-rw-r--r--docs/libcurl/symbols-in-versions1
-rw-r--r--include/curl/multi.h10
-rw-r--r--lib/http2.c27
5 files changed, 28 insertions, 21 deletions
diff --git a/docs/TODO b/docs/TODO
index 11e01e04d..bdf6a87b5 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -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
@@ -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
diff --git a/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3 b/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3
index 241f47547..a80b2539f 100644
--- a/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3
+++ b/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3
@@ -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
diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions
index ca2865725..c0cdbdc04 100644
--- a/docs/libcurl/symbols-in-versions
+++ b/docs/libcurl/symbols-in-versions
@@ -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
diff --git a/include/curl/multi.h b/include/curl/multi.h
index 2e6bb72d6..b911ba92d 100644
--- a/include/curl/multi.h
+++ b/include/curl/multi.h
@@ -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 */
diff --git a/lib/http2.c b/lib/http2.c
index e81dc8d01..e4fa9b0b3 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -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) {
@@ -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;
}
@@ -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,
@@ -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;
@@ -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;
}
@@ -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;
@@ -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));
+ 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;