Skip to content

Commit

Permalink
http2: initial implementation of the push callback
Browse files Browse the repository at this point in the history
  • Loading branch information
bagder committed Jun 24, 2015
1 parent 7019195 commit ea7134a
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 10 deletions.
2 changes: 1 addition & 1 deletion docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3
Expand Up @@ -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);

Expand Down
6 changes: 5 additions & 1 deletion include/curl/multi.h
Expand Up @@ -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);

Expand Down
80 changes: 74 additions & 6 deletions lib/http2.c
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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:
Expand Down
8 changes: 6 additions & 2 deletions lib/multi.c
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions lib/multihandle.h
Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions lib/urldata.h
Expand Up @@ -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. */
Expand Down

0 comments on commit ea7134a

Please sign in to comment.