diff --git a/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3 b/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3 index a46150ab71..0e4e3326e2 100644 --- a/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3 +++ b/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3 @@ -27,15 +27,8 @@ CURLMOPT_PUSHFUNCTION \- approve or deny server pushes .nf #include -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, @@ -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, diff --git a/include/curl/multi.h b/include/curl/multi.h index 5b462adc81..b2670e2547 100644 --- a/include/curl/multi.h +++ b/include/curl/multi.h @@ -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, diff --git a/lib/http.c b/lib/http.c index d307eabd55..f64a565469 100644 --- a/lib/http.c +++ b/lib/http.c @@ -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; @@ -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 diff --git a/lib/http.h b/lib/http.h index 80ec68303d..63ea4ace48 100644 --- a/lib/http.h +++ b/lib/http.h @@ -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 }; diff --git a/lib/http2.c b/lib/http2.c index 8f5b6930b6..674a39c09a 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -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; ipush_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; } @@ -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); @@ -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; }