]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
CURL_PUSH_ERROROUT: allow the push callback to fail the parent stream
authorDaniel Stenberg <daniel@haxx.se>
Wed, 15 Jul 2020 22:24:29 +0000 (00:24 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 15 Jul 2020 22:24:29 +0000 (00:24 +0200)
... 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

docs/TODO
docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3
docs/libcurl/symbols-in-versions
include/curl/multi.h
lib/http2.c

index 11e01e04d64677f77905a23fb1ab30251d8b7f60..bdf6a87b5d89b15fd660e80a71eb08d81f11704b 100644 (file)
--- 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
  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
index 241f47547811a9e37fa95c9861f852bab6f0e1f2..a80b2539f354838dab731aaa45b99077e299ff13 100644 (file)
@@ -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
index ca28657258a9582b914e814ec9ded549183fbca1..c0cdbdc04405fb72aa404a2ddd2e6c89e9a07436 100644 (file)
@@ -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
index 2e6bb72d648db7ced5be0dc9015dbf9001116d99..b911ba92dd04406254b7a41787cf0da8fe6c960d 100644 (file)
@@ -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 */
 
index e81dc8d013a01701843c5cbb6460bacc37ccf1bc..e4fa9b0b3bc92e5f3c8eecbe1bce4895442aacc7 100644 (file)
@@ -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;