]> granicus.if.org Git - curl/commitdiff
http2: initial implementation of the push callback
authorDaniel Stenberg <daniel@haxx.se>
Mon, 1 Jun 2015 09:45:52 +0000 (11:45 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 24 Jun 2015 21:44:42 +0000 (23:44 +0200)
docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3
include/curl/multi.h
lib/http2.c
lib/multi.c
lib/multihandle.h
lib/urldata.h

index 28fa2e76c071bdfc5d6bfec52c2023feff760115..a46150ab71009e8f267830908947dbc7b9ce53e5 100644 (file)
@@ -39,7 +39,7 @@ struct curl_headerpair *curl_pushheader_byname(push_headers, char *name);
 
 int curl_push_callback(CURL *parent,
                        CURL *easy,
-                       int num_headers,
+                       size_t num_headers,
                        struct curl_pushheaders *headers,
                        void *userp);
 
index ed3a3a793ebb0e3bc659fc18fe99a80547dda3f5..5b462adc8105b73f73dbb0327030fba9df223da9 100644 (file)
@@ -302,10 +302,14 @@ struct curl_headerpair {
 };
 
 struct curl_pushheaders;  /* forward declaration only */
+struct curl_headerpair *curl_pushheader_bynum(struct curl_pushheaders *h,
+                                              int num);
+struct curl_headerpair *curl_pushheader_byname(struct curl_pushheaders *h,
+                                               char *name);
 
 typedef int (*curl_push_callback)(CURL *parent,
                                   CURL *easy,
-                                  int num_headers,
+                                  size_t num_headers,
                                   struct curl_pushheaders *headers,
                                   void *userp);
 
index fa47d0ece16eae3192c4eb655dc313e15f1d33f6..ae8afa480447fe02e6315962653f84cccab64ad3 100644 (file)
@@ -33,6 +33,7 @@
 #include "rawstr.h"
 #include "multiif.h"
 #include "conncache.h"
+#include "url.h"
 
 /* The last #include files should be: */
 #include "curl_memory.h"
@@ -205,6 +206,71 @@ static ssize_t send_callback(nghttp2_session *h2,
   return written;
 }
 
+
+/* We pass a pointer to this struct in the push callback, but the contents of
+   the struct are hidden from the user. */
+struct curl_pushheaders {
+  struct SessionHandle *data;
+  const nghttp2_push_promise *frame;
+};
+
+/*
+ * push header access function. Only to be used from within the push callback
+ */
+struct curl_headerpair *curl_pushheader_bynum(struct curl_pushheaders *h,
+                                              int num)
+{
+  /* Verify that we got a good easy handle in the push header struct, mostly to
+     detect rubbish input fast(er). */
+  if(!h || !GOOD_EASY_HANDLE(h->data))
+    return NULL;
+  (void)num;
+  return NULL;
+}
+
+static int push_promise(struct SessionHandle *data,
+                        const nghttp2_push_promise *frame)
+{
+  int rv;
+  if(data->multi->push_cb) {
+    /* clone the parent */
+    CURL *newhandle = curl_easy_duphandle(data);
+    if(!newhandle) {
+      infof(data, "failed to duplicate handle\n");
+      rv = 1; /* FAIL HARD */
+    }
+    else {
+      struct curl_pushheaders heads;
+      heads.data = data;
+      heads.frame = frame;
+      /* ask the application */
+      DEBUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
+      rv = data->multi->push_cb(data, newhandle,
+                                frame->nvlen, &heads,
+                                data->multi->push_userp);
+      if(rv)
+        /* denied, kill off the new handle again */
+        (void)Curl_close(newhandle);
+      else {
+        /* approved, add to the multi handle */
+        CURLMcode rc = curl_multi_add_handle(data->multi, newhandle);
+        if(rc) {
+          infof(data, "failed to add handle to multi\n");
+          Curl_close(newhandle);
+          rv = 1;
+        }
+        else
+          rv = 0;
+      }
+    }
+  }
+  else {
+    DEBUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
+    rv = 1;
+  }
+  return rv;
+}
+
 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
                          void *userp)
 {
@@ -292,12 +358,14 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
     Curl_expire(data_s, 1);
     break;
   case NGHTTP2_PUSH_PROMISE:
-    DEBUGF(infof(data_s, "Got PUSH_PROMISE, RST_STREAM it!\n"));
-    rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
-                                   frame->push_promise.promised_stream_id,
-                                   NGHTTP2_CANCEL);
-    if(nghttp2_is_fatal(rv)) {
-      return rv;
+    rv = push_promise(data_s, &frame->push_promise);
+    if(rv) { /* deny! */
+      rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
+                                     frame->push_promise.promised_stream_id,
+                                     NGHTTP2_CANCEL);
+      if(nghttp2_is_fatal(rv)) {
+        return rv;
+      }
     }
     break;
   case NGHTTP2_SETTINGS:
index a8d3e38b59c013b2db4ddbab616775b01978176c..33c03f299fda672c1ce0bbac67bf4d75340fc017 100644 (file)
@@ -62,8 +62,6 @@
 
 #define GOOD_MULTI_HANDLE(x) \
   ((x) && (((struct Curl_multi *)(x))->type == CURL_MULTI_HANDLE))
-#define GOOD_EASY_HANDLE(x) \
-  ((x) && (((struct SessionHandle *)(x))->magic == CURLEASY_MAGIC_NUMBER))
 
 static void singlesocket(struct Curl_multi *multi,
                          struct SessionHandle *data);
@@ -2341,6 +2339,12 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle,
   case CURLMOPT_SOCKETDATA:
     multi->socket_userp = va_arg(param, void *);
     break;
+  case CURLMOPT_PUSHFUNCTION:
+    multi->push_cb = va_arg(param, curl_push_callback);
+    break;
+  case CURLMOPT_PUSHDATA:
+    multi->push_userp = va_arg(param, void *);
+    break;
   case CURLMOPT_PIPELINING:
     multi->pipelining = va_arg(param, long);
     break;
index cad44d1df5af9a0d7f817d44db133038eed53ada..6c24f50f1e1f7494ed656d108f034fa64b6e9276 100644 (file)
@@ -87,6 +87,10 @@ struct Curl_multi {
   curl_socket_callback socket_cb;
   void *socket_userp;
 
+  /* callback function and user data pointer for server push */
+  curl_push_callback push_cb;
+  void *push_userp;
+
   /* Hostname cache */
   struct curl_hash hostcache;
 
index 05bda794b744b123e9591cd16a85b1a7abe32808..59c704e0da6f5cf2d5c1b4d3446d850d156273f8 100644 (file)
 #define HEADERSIZE 256
 
 #define CURLEASY_MAGIC_NUMBER 0xc0dedbadU
+#define GOOD_EASY_HANDLE(x) \
+  ((x) && (((struct SessionHandle *)(x))->magic == CURLEASY_MAGIC_NUMBER))
 
 /* Some convenience macros to get the larger/smaller value out of two given.
    We prefix with CURL to prevent name collisions. */