summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2015-06-01 11:45:52 +0200
committerDaniel Stenberg <daniel@haxx.se>2015-06-24 23:44:42 +0200
commitea7134ac874a66107e54ff93657ac565cf2ec4aa (patch)
tree72f0b2d2b5aa7158197449ef46c1cb122e010476
parent70191958b5db1814e13ef90856a9f70a5186e733 (diff)
downloadcurl-ea7134ac874a66107e54ff93657ac565cf2ec4aa.tar.gz
http2: initial implementation of the push callback
-rw-r--r--docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.32
-rw-r--r--include/curl/multi.h6
-rw-r--r--lib/http2.c80
-rw-r--r--lib/multi.c8
-rw-r--r--lib/multihandle.h4
-rw-r--r--lib/urldata.h2
6 files changed, 92 insertions, 10 deletions
diff --git a/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3 b/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3
index 28fa2e76c..a46150ab7 100644
--- a/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3
+++ b/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3
@@ -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);
diff --git a/include/curl/multi.h b/include/curl/multi.h
index ed3a3a793..5b462adc8 100644
--- a/include/curl/multi.h
+++ b/include/curl/multi.h
@@ -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);
diff --git a/lib/http2.c b/lib/http2.c
index fa47d0ece..ae8afa480 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -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:
diff --git a/lib/multi.c b/lib/multi.c
index a8d3e38b5..33c03f299 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -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;
diff --git a/lib/multihandle.h b/lib/multihandle.h
index cad44d1df..6c24f50f1 100644
--- a/lib/multihandle.h
+++ b/lib/multihandle.h
@@ -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;
diff --git a/lib/urldata.h b/lib/urldata.h
index 05bda794b..59c704e0d 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -198,6 +198,8 @@
#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. */