Skip to content

Commit

Permalink
http2: fixed the header accessor functions for the push callback
Browse files Browse the repository at this point in the history
  • Loading branch information
bagder committed Jun 24, 2015
1 parent feea926 commit f65ab88
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 43 deletions.
21 changes: 7 additions & 14 deletions docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3
Expand Up @@ -27,15 +27,8 @@ CURLMOPT_PUSHFUNCTION \- approve or deny server pushes
.nf
#include <curl/curl.h>

struct curl_headerpair {
unsigned char *name; /* zero terminated name */
size_t namelen; /* length of 'name' */
unsigned char *value; /* zero terminated name */
size_t valuelen; /* length of 'value' */
};

struct curl_headerpair *curl_pushheader_bynum(push_headers, int num);
struct curl_headerpair *curl_pushheader_byname(push_headers, char *name);
char *curl_pushheader_bynum(push_headers, int num);
char *curl_pushheader_byname(push_headers, char *name);

int curl_push_callback(CURL *parent,
CURL *easy,
Expand Down Expand Up @@ -78,12 +71,12 @@ functions. These functions can only be used from within this callback and they
can only access the PUSH_PROMISE headers. The normal response headers will be
pased to the header callback for pushed streams just as for normal streams.
.IP curl_pushheader_bynum
Returns the header pair at index 'num' (or NULL). The returned pointer points
to a struct that will be freed when this callback returns.
Returns the header at index 'num' (or NULL). The returned pointer points
to a "name:value" string that will be freed when this callback returns.
.IP curl_pushheader_byname
Returns the header pair for the given header name (or NULL). This is a
shortcut so that the application doesn't have to loop through all headers to
find the one it is interested in.
Returns the value for the given header name (or NULL). This is a shortcut so
that the application doesn't have to loop through all headers to find the one
it is interested in.
.SH CALLBACK RETURN VALUE
.IP "CURL_PUSH_OK (0)"
The application has accepted the stream and it can now start receiving data,
Expand Down
17 changes: 6 additions & 11 deletions include/curl/multi.h
Expand Up @@ -294,18 +294,13 @@ typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */
#define CURL_PUSH_OK 0
#define CURL_PUSH_DENY 1

struct curl_headerpair {
unsigned char *name; /* zero terminated name */
size_t namelen; /* length of 'name' */
unsigned char *value; /* zero terminated name */
size_t valuelen; /* length of 'value' */
};

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);

CURL_EXTERN char *curl_pushheader_bynum(struct curl_pushheaders *h,
size_t num);

CURL_EXTERN char *curl_pushheader_byname(struct curl_pushheaders *h,
char *name);

typedef int (*curl_push_callback)(CURL *parent,
CURL *easy,
Expand Down
4 changes: 4 additions & 0 deletions lib/http.c
Expand Up @@ -176,6 +176,8 @@ static CURLcode http_disconnect(struct connectdata *conn, bool dead_connection)
if(http) {
Curl_add_buffer_free(http->header_recvbuf);
http->header_recvbuf = NULL; /* clear the pointer */
free(http->push_headers);
http->push_headers = NULL;
}
#else
(void)conn;
Expand Down Expand Up @@ -1492,6 +1494,8 @@ CURLcode Curl_http_done(struct connectdata *conn,
DEBUGF(infof(data, "free header_recvbuf!!\n"));
Curl_add_buffer_free(http->header_recvbuf);
http->header_recvbuf = NULL; /* clear the pointer */
free(http->push_headers);
http->push_headers = NULL;
}
#endif

Expand Down
5 changes: 4 additions & 1 deletion lib/http.h
Expand Up @@ -176,7 +176,10 @@ struct HTTP {
const uint8_t *upload_mem; /* points to a buffer to read from */
size_t upload_len; /* size of the buffer 'upload_mem' points to */
curl_off_t upload_left; /* number of bytes left to upload */
Curl_send_buffer *push_recvbuf; /* store incoming push headers */

char **push_headers; /* allocated array */
size_t push_headers_used; /* number of entries filled in */
size_t push_headers_alloc; /* number of entries allocated */
#endif
};

Expand Down
78 changes: 61 additions & 17 deletions lib/http2.c
Expand Up @@ -219,14 +219,42 @@ struct curl_pushheaders {
/*
* 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)
char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t 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;
else {
struct HTTP *stream = h->data->req.protop;
if(num < stream->push_headers_used)
return stream->push_headers[num];
}
return NULL;
}

/*
* push header access function. Only to be used from within the push callback
*/
char *curl_pushheader_byname(struct curl_pushheaders *h, char *header)
{
/* 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) || !header)
return NULL;
else {
struct HTTP *stream = h->data->req.protop;
size_t len = strlen(header);
size_t i;
for(i=0; i<stream->push_headers_used; i++) {
if(!strncmp(header, stream->push_headers[i], len)) {
/* sub-match, make sure that it us followed by a colon */
if(stream->push_headers[i][len] != ':')
continue;
return &stream->push_headers[i][len+1];
}
}
}
return NULL;
}

Expand Down Expand Up @@ -283,13 +311,14 @@ static int push_promise(struct SessionHandle *data,

stream = data->req.protop;

#ifdef CURLDEBUG
fprintf(stderr, "PUSHHDR %s\n", stream->push_recvbuf->buffer);
#endif

rv = data->multi->push_cb(data, newhandle,
frame->nvlen, &heads,
stream->push_headers_used, &heads,
data->multi->push_userp);

/* free the headers array again */
free(stream->push_headers);
stream->push_headers = NULL;

if(rv) {
/* denied, kill off the new handle again */
(void)Curl_close(newhandle);
Expand Down Expand Up @@ -667,15 +696,30 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
/* Store received PUSH_PROMISE headers to be used when the subsequent
PUSH_PROMISE callback comes */
if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
fprintf(stderr, "*** PUSH_PROMISE headers on stream %u for %u\n",
stream_id,
frame->push_promise.promised_stream_id);
if(!stream->push_recvbuf)
stream->push_recvbuf = Curl_add_buffer_init();
Curl_add_buffer(stream->push_recvbuf, name, namelen);
Curl_add_buffer(stream->push_recvbuf, ":", 1);
Curl_add_buffer(stream->push_recvbuf, value, valuelen);
Curl_add_buffer(stream->push_recvbuf, "\r\n", 2);
char *h;

if(!stream->push_headers) {
stream->push_headers_alloc = 10;
stream->push_headers = malloc(stream->push_headers_alloc *
sizeof(char *));
stream->push_headers_used = 0;
}
else if(stream->push_headers_used ==
stream->push_headers_alloc) {
char **headp;
stream->push_headers_alloc *= 2;
headp = realloc(stream->push_headers,
stream->push_headers_alloc * sizeof(char *));
if(!headp) {
free(stream->push_headers);
stream->push_headers = NULL;
return 1;
}
stream->push_headers = headp;
}
h = aprintf("%s:%s", name, value);
if(h)
stream->push_headers[stream->push_headers_used++] = h;
return 0;
}

Expand Down

0 comments on commit f65ab88

Please sign in to comment.