Compare commits
15 Commits
main
...
http2-push
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f87a3d736f | ||
![]() |
bf5218c85e | ||
![]() |
c2cc3a5e97 | ||
![]() |
aa4e3c6438 | ||
![]() |
d712e22b56 | ||
![]() |
f649411a1c | ||
![]() |
cb5d4b1389 | ||
![]() |
3174c940b5 | ||
![]() |
2b3860d1d6 | ||
![]() |
0a9f285140 | ||
![]() |
af3d76ccf9 | ||
![]() |
952b745c98 | ||
![]() |
21784936e1 | ||
![]() |
352fbceef3 | ||
![]() |
19d5bcd66a |
@ -32,7 +32,7 @@ check_PROGRAMS = 10-at-a-time anyauthput cookie_interface debug fileupload \
|
|||||||
imap-list imap-lsub imap-fetch imap-store imap-append imap-examine \
|
imap-list imap-lsub imap-fetch imap-store imap-append imap-examine \
|
||||||
imap-search imap-create imap-delete imap-copy imap-noop imap-ssl \
|
imap-search imap-create imap-delete imap-copy imap-noop imap-ssl \
|
||||||
imap-tls imap-multi url2file sftpget ftpsget postinmemory http2-download \
|
imap-tls imap-multi url2file sftpget ftpsget postinmemory http2-download \
|
||||||
http2-upload
|
http2-upload http2-serverpush
|
||||||
|
|
||||||
# These examples require external dependencies that may not be commonly
|
# These examples require external dependencies that may not be commonly
|
||||||
# available on POSIX systems, so don't bother attempting to compile them here.
|
# available on POSIX systems, so don't bother attempting to compile them here.
|
||||||
|
313
docs/examples/http2-serverpush.c
Normal file
313
docs/examples/http2-serverpush.c
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* _ _ ____ _
|
||||||
|
* Project ___| | | | _ \| |
|
||||||
|
* / __| | | | |_) | |
|
||||||
|
* | (__| |_| | _ <| |___
|
||||||
|
* \___|\___/|_| \_\_____|
|
||||||
|
*
|
||||||
|
* Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
|
*
|
||||||
|
* This software is licensed as described in the file COPYING, which
|
||||||
|
* you should have received as part of this distribution. The terms
|
||||||
|
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||||
|
*
|
||||||
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||||
|
* copies of the Software, and permit persons to whom the Software is
|
||||||
|
* furnished to do so, under the terms of the COPYING file.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* somewhat unix-specific */
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/* curl stuff */
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
#ifndef CURLPIPE_MULTIPLEX
|
||||||
|
#error "too old libcurl, can't do HTTP/2 server push!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static
|
||||||
|
void dump(const char *text, unsigned char *ptr, size_t size,
|
||||||
|
char nohex)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
size_t c;
|
||||||
|
|
||||||
|
unsigned int width=0x10;
|
||||||
|
|
||||||
|
if(nohex)
|
||||||
|
/* without the hex output, we can fit more on screen */
|
||||||
|
width = 0x40;
|
||||||
|
|
||||||
|
fprintf(stderr, "%s, %ld bytes (0x%lx)\n",
|
||||||
|
text, (long)size, (long)size);
|
||||||
|
|
||||||
|
for(i=0; i<size; i+= width) {
|
||||||
|
|
||||||
|
fprintf(stderr, "%4.4lx: ", (long)i);
|
||||||
|
|
||||||
|
if(!nohex) {
|
||||||
|
/* hex not disabled, show it */
|
||||||
|
for(c = 0; c < width; c++)
|
||||||
|
if(i+c < size)
|
||||||
|
fprintf(stderr, "%02x ", ptr[i+c]);
|
||||||
|
else
|
||||||
|
fputs(" ", stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(c = 0; (c < width) && (i+c < size); c++) {
|
||||||
|
/* check for 0D0A; if found, skip past and start a new line of output */
|
||||||
|
if (nohex && (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) {
|
||||||
|
i+=(c+2-width);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%c",
|
||||||
|
(ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:'.');
|
||||||
|
/* check again for 0D0A, to avoid an extra \n if it's at width */
|
||||||
|
if (nohex && (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) {
|
||||||
|
i+=(c+3-width);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fputc('\n', stderr); /* newline */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int my_trace(CURL *handle, curl_infotype type,
|
||||||
|
char *data, size_t size,
|
||||||
|
void *userp)
|
||||||
|
{
|
||||||
|
const char *text;
|
||||||
|
(void)handle; /* prevent compiler warning */
|
||||||
|
(void)userp;
|
||||||
|
switch (type) {
|
||||||
|
case CURLINFO_TEXT:
|
||||||
|
fprintf(stderr, "== Info: %s", data);
|
||||||
|
default: /* in case a new one is introduced to shock us */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case CURLINFO_HEADER_OUT:
|
||||||
|
text = "=> Send header";
|
||||||
|
break;
|
||||||
|
case CURLINFO_DATA_OUT:
|
||||||
|
text = "=> Send data";
|
||||||
|
break;
|
||||||
|
case CURLINFO_SSL_DATA_OUT:
|
||||||
|
text = "=> Send SSL data";
|
||||||
|
break;
|
||||||
|
case CURLINFO_HEADER_IN:
|
||||||
|
text = "<= Recv header";
|
||||||
|
break;
|
||||||
|
case CURLINFO_DATA_IN:
|
||||||
|
text = "<= Recv data";
|
||||||
|
break;
|
||||||
|
case CURLINFO_SSL_DATA_IN:
|
||||||
|
text = "<= Recv SSL data";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
dump(text, (unsigned char *)data, size, 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setup(CURL *hnd)
|
||||||
|
{
|
||||||
|
FILE *out = fopen("dl", "wb");
|
||||||
|
|
||||||
|
/* write to this file */
|
||||||
|
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, out);
|
||||||
|
|
||||||
|
/* set the same URL */
|
||||||
|
curl_easy_setopt(hnd, CURLOPT_URL, "https://localhost:8443/index.html");
|
||||||
|
|
||||||
|
/* send it verbose for max debuggaility */
|
||||||
|
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
|
||||||
|
curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace);
|
||||||
|
|
||||||
|
/* HTTP/2 please */
|
||||||
|
curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
|
||||||
|
|
||||||
|
/* we use a self-signed test server, skip verification during debugging */
|
||||||
|
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||||
|
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||||
|
|
||||||
|
#if (CURLPIPE_MULTIPLEX > 0)
|
||||||
|
/* wait for pipe connection to confirm */
|
||||||
|
curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* called when there's an incoming push */
|
||||||
|
static int server_push_callback(CURL *parent,
|
||||||
|
CURL *easy,
|
||||||
|
size_t num_headers,
|
||||||
|
struct curl_pushheaders *headers,
|
||||||
|
void *userp)
|
||||||
|
{
|
||||||
|
char *headp;
|
||||||
|
size_t i;
|
||||||
|
int *transfers = (int *)userp;
|
||||||
|
char filename[128];
|
||||||
|
FILE *out;
|
||||||
|
static unsigned int count = 0;
|
||||||
|
|
||||||
|
(void)parent; /* we have no use for this */
|
||||||
|
|
||||||
|
sprintf(filename, "push%u", count++);
|
||||||
|
|
||||||
|
/* here's a new stream, save it in a new file for each new push */
|
||||||
|
out = fopen(filename, "wb");
|
||||||
|
|
||||||
|
/* write to this file */
|
||||||
|
curl_easy_setopt(easy, CURLOPT_WRITEDATA, out);
|
||||||
|
|
||||||
|
fprintf(stderr, "**** push callback approves stream %u, got %d headers!\n",
|
||||||
|
count, (int)num_headers);
|
||||||
|
|
||||||
|
for(i=0; i<num_headers; i++) {
|
||||||
|
headp = curl_pushheader_bynum(headers, i);
|
||||||
|
fprintf(stderr, "**** header %u: %s\n", (int)i, headp);
|
||||||
|
}
|
||||||
|
|
||||||
|
headp = curl_pushheader_byname(headers, ":path");
|
||||||
|
if(headp) {
|
||||||
|
fprintf(stderr, "**** The PATH is %s\n", headp /* skip :path + colon */ );
|
||||||
|
}
|
||||||
|
|
||||||
|
(*transfers)++; /* one more */
|
||||||
|
return CURL_PUSH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Download a file over HTTP/2, take care of server push.
|
||||||
|
*/
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
CURL *easy;
|
||||||
|
CURLM *multi_handle;
|
||||||
|
int still_running; /* keep number of running handles */
|
||||||
|
int transfers=1; /* we start with one */
|
||||||
|
struct CURLMsg *m;
|
||||||
|
|
||||||
|
/* init a multi stack */
|
||||||
|
multi_handle = curl_multi_init();
|
||||||
|
|
||||||
|
easy = curl_easy_init();
|
||||||
|
|
||||||
|
/* set options */
|
||||||
|
setup(easy);
|
||||||
|
|
||||||
|
/* add the easy transfer */
|
||||||
|
curl_multi_add_handle(multi_handle, easy);
|
||||||
|
|
||||||
|
curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
|
||||||
|
curl_multi_setopt(multi_handle, CURLMOPT_PUSHFUNCTION, server_push_callback);
|
||||||
|
curl_multi_setopt(multi_handle, CURLMOPT_PUSHDATA, &transfers);
|
||||||
|
|
||||||
|
/* we start some action by calling perform right away */
|
||||||
|
curl_multi_perform(multi_handle, &still_running);
|
||||||
|
|
||||||
|
do {
|
||||||
|
struct timeval timeout;
|
||||||
|
int rc; /* select() return code */
|
||||||
|
CURLMcode mc; /* curl_multi_fdset() return code */
|
||||||
|
|
||||||
|
fd_set fdread;
|
||||||
|
fd_set fdwrite;
|
||||||
|
fd_set fdexcep;
|
||||||
|
int maxfd = -1;
|
||||||
|
|
||||||
|
long curl_timeo = -1;
|
||||||
|
|
||||||
|
FD_ZERO(&fdread);
|
||||||
|
FD_ZERO(&fdwrite);
|
||||||
|
FD_ZERO(&fdexcep);
|
||||||
|
|
||||||
|
/* set a suitable timeout to play around with */
|
||||||
|
timeout.tv_sec = 1;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
|
||||||
|
curl_multi_timeout(multi_handle, &curl_timeo);
|
||||||
|
if(curl_timeo >= 0) {
|
||||||
|
timeout.tv_sec = curl_timeo / 1000;
|
||||||
|
if(timeout.tv_sec > 1)
|
||||||
|
timeout.tv_sec = 1;
|
||||||
|
else
|
||||||
|
timeout.tv_usec = (curl_timeo % 1000) * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get file descriptors from the transfers */
|
||||||
|
mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
|
||||||
|
|
||||||
|
if(mc != CURLM_OK) {
|
||||||
|
fprintf(stderr, "curl_multi_fdset() failed, code %d.\n", mc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* On success the value of maxfd is guaranteed to be >= -1. We call
|
||||||
|
select(maxfd + 1, ...); specially in case of (maxfd == -1) there are
|
||||||
|
no fds ready yet so we call select(0, ...) --or Sleep() on Windows--
|
||||||
|
to sleep 100ms, which is the minimum suggested value in the
|
||||||
|
curl_multi_fdset() doc. */
|
||||||
|
|
||||||
|
if(maxfd == -1) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
Sleep(100);
|
||||||
|
rc = 0;
|
||||||
|
#else
|
||||||
|
/* Portable sleep for platforms other than Windows. */
|
||||||
|
struct timeval wait = { 0, 100 * 1000 }; /* 100ms */
|
||||||
|
rc = select(0, NULL, NULL, NULL, &wait);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Note that on some platforms 'timeout' may be modified by select().
|
||||||
|
If you need access to the original value save a copy beforehand. */
|
||||||
|
rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(rc) {
|
||||||
|
case -1:
|
||||||
|
/* select error */
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
default:
|
||||||
|
/* timeout or readable/writable sockets */
|
||||||
|
curl_multi_perform(multi_handle, &still_running);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A little caution when doing server push is that libcurl itself has
|
||||||
|
* created and added one or more easy handles but we need to clean them up
|
||||||
|
* when we are done.
|
||||||
|
*/
|
||||||
|
|
||||||
|
do {
|
||||||
|
int msgq = 0;;
|
||||||
|
m = curl_multi_info_read(multi_handle, &msgq);
|
||||||
|
if(m && (m->msg == CURLMSG_DONE)) {
|
||||||
|
CURL *e = m->easy_handle;
|
||||||
|
transfers--;
|
||||||
|
curl_multi_remove_handle(multi_handle, e);
|
||||||
|
curl_easy_cleanup(e);
|
||||||
|
}
|
||||||
|
} while(m);
|
||||||
|
|
||||||
|
} while(transfers); /* as long as we have transfers going */
|
||||||
|
|
||||||
|
curl_multi_cleanup(multi_handle);
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
49
docs/libcurl/opts/CURLMOPT_PUSHDATA.3
Normal file
49
docs/libcurl/opts/CURLMOPT_PUSHDATA.3
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
.\" **************************************************************************
|
||||||
|
.\" * _ _ ____ _
|
||||||
|
.\" * Project ___| | | | _ \| |
|
||||||
|
.\" * / __| | | | |_) | |
|
||||||
|
.\" * | (__| |_| | _ <| |___
|
||||||
|
.\" * \___|\___/|_| \_\_____|
|
||||||
|
.\" *
|
||||||
|
.\" * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
|
.\" *
|
||||||
|
.\" * This software is licensed as described in the file COPYING, which
|
||||||
|
.\" * you should have received as part of this distribution. The terms
|
||||||
|
.\" * are also available at http://curl.haxx.se/docs/copyright.html.
|
||||||
|
.\" *
|
||||||
|
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||||
|
.\" * copies of the Software, and permit persons to whom the Software is
|
||||||
|
.\" * furnished to do so, under the terms of the COPYING file.
|
||||||
|
.\" *
|
||||||
|
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
.\" * KIND, either express or implied.
|
||||||
|
.\" *
|
||||||
|
.\" **************************************************************************
|
||||||
|
.\"
|
||||||
|
.TH CURLMOPT_PUSHDATA 3 "1 Jun 2015" "libcurl 7.44.0" "curl_multi_setopt options"
|
||||||
|
.SH NAME
|
||||||
|
CURLMOPT_PUSHDATA \- pointer to pass to push callback
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.nf
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_PUSHDATA, void *pointer);
|
||||||
|
.fi
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Set \fIpointer\fP to pass as the last argument to the
|
||||||
|
\fICURLMOPT_PUSHFUNCTION(3)\fP callback. The pointer will not be touched or
|
||||||
|
used by libcurl itself, only passed on to the callback function.
|
||||||
|
.SH DEFAULT
|
||||||
|
NULL
|
||||||
|
.SH PROTOCOLS
|
||||||
|
HTTP(S)
|
||||||
|
.SH EXAMPLE
|
||||||
|
TODO
|
||||||
|
.SH AVAILABILITY
|
||||||
|
Added in 7.44.0
|
||||||
|
.SH RETURN VALUE
|
||||||
|
Returns CURLM_OK if the option is supported, and CURLM_UNKNOWN_OPTION if not.
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.BR CURLMOPT_PUSHFUNCTION "(3), " CURLMOPT_PIPELINING "(3), "
|
||||||
|
.BR CURLOPT_PIPEWAIT "(3), "
|
||||||
|
.BR RFC 7540
|
132
docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3
Normal file
132
docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.3
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
.\" **************************************************************************
|
||||||
|
.\" * _ _ ____ _
|
||||||
|
.\" * Project ___| | | | _ \| |
|
||||||
|
.\" * / __| | | | |_) | |
|
||||||
|
.\" * | (__| |_| | _ <| |___
|
||||||
|
.\" * \___|\___/|_| \_\_____|
|
||||||
|
.\" *
|
||||||
|
.\" * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
|
.\" *
|
||||||
|
.\" * This software is licensed as described in the file COPYING, which
|
||||||
|
.\" * you should have received as part of this distribution. The terms
|
||||||
|
.\" * are also available at http://curl.haxx.se/docs/copyright.html.
|
||||||
|
.\" *
|
||||||
|
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||||
|
.\" * copies of the Software, and permit persons to whom the Software is
|
||||||
|
.\" * furnished to do so, under the terms of the COPYING file.
|
||||||
|
.\" *
|
||||||
|
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
.\" * KIND, either express or implied.
|
||||||
|
.\" *
|
||||||
|
.\" **************************************************************************
|
||||||
|
.\"
|
||||||
|
.TH CURLMOPT_PUSHFUNCTION 3 "1 Jun 2015" "libcurl 7.44.0" "curl_multi_setopt options"
|
||||||
|
.SH NAME
|
||||||
|
CURLMOPT_PUSHFUNCTION \- callback that approves or denies server pushes
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.nf
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
char *curl_pushheader_bynum(push_headers, int num);
|
||||||
|
char *curl_pushheader_byname(push_headers, const char *name);
|
||||||
|
|
||||||
|
int curl_push_callback(CURL *parent,
|
||||||
|
CURL *easy,
|
||||||
|
size_t num_headers,
|
||||||
|
struct curl_pushheaders *headers,
|
||||||
|
void *userp);
|
||||||
|
|
||||||
|
CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_PUSHFUNCTION,
|
||||||
|
curl_push_callback func);
|
||||||
|
.fi
|
||||||
|
.SH DESCRIPTION
|
||||||
|
This callback gets called when a new HTTP/2 stream is being pushed by the
|
||||||
|
server (using the PUSH_PROMISE frame). If no push callback is set, all offered
|
||||||
|
pushes will be denied automatically.
|
||||||
|
.SH CALLBACK DESCRIPTION
|
||||||
|
The callback gets its arguments like this:
|
||||||
|
|
||||||
|
\fIparent\fP is the handle of the stream on which this push arrives. The new
|
||||||
|
handle has been duphandle()d from the parent, meaning that it has gotten all
|
||||||
|
its options inherited. It is then up to the application to alter any options
|
||||||
|
if desired.
|
||||||
|
|
||||||
|
\fIeasy\fP is a newly created handle that represents this upcoming transfer.
|
||||||
|
|
||||||
|
\fInum_headers\fP is the number of name+value pairs that was received and can
|
||||||
|
be accessed
|
||||||
|
|
||||||
|
\fIheaders\fP is a handle used to access push headers using the accessor
|
||||||
|
functions described below. This only accesses and provides the PUSH_PROMISE
|
||||||
|
headers, the normal response headers will be provided in the header callback
|
||||||
|
as usual.
|
||||||
|
|
||||||
|
\fIuserp\fP is the pointer set with \fICURLMOPT_PUSHDATA(3)\fP
|
||||||
|
|
||||||
|
If the callback returns CURL_PUSH_OK, the 'easy' handle will be added to the
|
||||||
|
multi handle, the callback must not do that by itself.
|
||||||
|
|
||||||
|
The callback can access PUSH_PROMISE headers with two accessor
|
||||||
|
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 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 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. The data pointed will be freed when this callback
|
||||||
|
returns.
|
||||||
|
.SH CALLBACK RETURN VALUE
|
||||||
|
.IP "CURL_PUSH_OK (0)"
|
||||||
|
The application has accepted the stream and it can now start receiving data,
|
||||||
|
the ownership of the CURL handle has been taken over by the application.
|
||||||
|
.IP "CURL_PUSH_DENY (1)"
|
||||||
|
The callback denies the stream and no data for this will reach the
|
||||||
|
application, the easy handle will be destroyed by libcurl.
|
||||||
|
.IP *
|
||||||
|
All other return codes are reserved for future use.
|
||||||
|
.SH DEFAULT
|
||||||
|
NULL, no callback
|
||||||
|
.SH PROTOCOLS
|
||||||
|
HTTP(S) (HTTP/2 only)
|
||||||
|
.SH EXAMPLE
|
||||||
|
.nf
|
||||||
|
/* only allow pushes for file names starting with "push-" */
|
||||||
|
int push_callback(CURL *parent,
|
||||||
|
CURL *easy,
|
||||||
|
size_t num_headers,
|
||||||
|
struct curl_pushheaders *headers,
|
||||||
|
void *userp)
|
||||||
|
{
|
||||||
|
char *headp;
|
||||||
|
int *transfers = (int *)userp;
|
||||||
|
FILE *out;
|
||||||
|
headp = curl_pushheader_byname(headers, ":path");
|
||||||
|
if(headp && !strncmp(headp, "/push-", 6)) {
|
||||||
|
fprintf(stderr, "The PATH is %s\n", headp);
|
||||||
|
|
||||||
|
/* save the push here */
|
||||||
|
out = fopen("pushed-stream", "wb");
|
||||||
|
|
||||||
|
/* write to this file */
|
||||||
|
curl_easy_setopt(easy, CURLOPT_WRITEDATA, out);
|
||||||
|
|
||||||
|
(*transfers)++; /* one more */
|
||||||
|
|
||||||
|
return CURL_PUSH_OK;
|
||||||
|
}
|
||||||
|
return CURL_PUSH_DENY;
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_multi_setopt(multi, CURLMOPT_PUSHFUNCTION, push_callback);
|
||||||
|
curl_multi_setopt(multi, CURLMOPT_PUSHDATA, &counter);
|
||||||
|
.fi
|
||||||
|
.SH AVAILABILITY
|
||||||
|
Added in 7.44.0
|
||||||
|
.SH RETURN VALUE
|
||||||
|
Returns CURLM_OK if the option is supported, and CURLM_UNKNOWN_OPTION if not.
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.BR CURLMOPT_PUSHDATA "(3), " CURLMOPT_PIPELINING "(3), " CURLOPT_PIPEWAIT "(3), "
|
||||||
|
.BR RFC 7540
|
@ -114,7 +114,8 @@ man_MANS = CURLOPT_ACCEPT_ENCODING.3 CURLOPT_ACCEPTTIMEOUT_MS.3 \
|
|||||||
CURLMOPT_SOCKETDATA.3 CURLMOPT_SOCKETFUNCTION.3 CURLMOPT_TIMERDATA.3 \
|
CURLMOPT_SOCKETDATA.3 CURLMOPT_SOCKETFUNCTION.3 CURLMOPT_TIMERDATA.3 \
|
||||||
CURLMOPT_TIMERFUNCTION.3 CURLOPT_UNIX_SOCKET_PATH.3 \
|
CURLMOPT_TIMERFUNCTION.3 CURLOPT_UNIX_SOCKET_PATH.3 \
|
||||||
CURLOPT_PATH_AS_IS.3 CURLOPT_PROXY_SERVICE_NAME.3 \
|
CURLOPT_PATH_AS_IS.3 CURLOPT_PROXY_SERVICE_NAME.3 \
|
||||||
CURLOPT_SERVICE_NAME.3 CURLOPT_PIPEWAIT.3
|
CURLOPT_SERVICE_NAME.3 CURLOPT_PIPEWAIT.3 CURLMOPT_PUSHDATA.3 \
|
||||||
|
CURLMOPT_PUSHFUNCTION.3
|
||||||
|
|
||||||
HTMLPAGES = CURLOPT_ACCEPT_ENCODING.html CURLOPT_ACCEPTTIMEOUT_MS.html \
|
HTMLPAGES = CURLOPT_ACCEPT_ENCODING.html CURLOPT_ACCEPTTIMEOUT_MS.html \
|
||||||
CURLOPT_ADDRESS_SCOPE.html CURLOPT_APPEND.html \
|
CURLOPT_ADDRESS_SCOPE.html CURLOPT_APPEND.html \
|
||||||
@ -222,7 +223,8 @@ HTMLPAGES = CURLOPT_ACCEPT_ENCODING.html CURLOPT_ACCEPTTIMEOUT_MS.html \
|
|||||||
CURLMOPT_TIMERDATA.html CURLMOPT_TIMERFUNCTION.html \
|
CURLMOPT_TIMERDATA.html CURLMOPT_TIMERFUNCTION.html \
|
||||||
CURLOPT_UNIX_SOCKET_PATH.html CURLOPT_PATH_AS_IS.html \
|
CURLOPT_UNIX_SOCKET_PATH.html CURLOPT_PATH_AS_IS.html \
|
||||||
CURLOPT_PROXY_SERVICE_NAME.html CURLOPT_SERVICE_NAME.html \
|
CURLOPT_PROXY_SERVICE_NAME.html CURLOPT_SERVICE_NAME.html \
|
||||||
CURLOPT_PIPEWAIT.html
|
CURLOPT_PIPEWAIT.html CURLMOPT_PUSHDATA.html \
|
||||||
|
CURLMOPT_PUSHFUNCTION.html
|
||||||
|
|
||||||
PDFPAGES = CURLOPT_ACCEPT_ENCODING.pdf CURLOPT_ACCEPTTIMEOUT_MS.pdf \
|
PDFPAGES = CURLOPT_ACCEPT_ENCODING.pdf CURLOPT_ACCEPTTIMEOUT_MS.pdf \
|
||||||
CURLOPT_ADDRESS_SCOPE.pdf CURLOPT_APPEND.pdf CURLOPT_AUTOREFERER.pdf \
|
CURLOPT_ADDRESS_SCOPE.pdf CURLOPT_APPEND.pdf CURLOPT_AUTOREFERER.pdf \
|
||||||
@ -328,7 +330,7 @@ PDFPAGES = CURLOPT_ACCEPT_ENCODING.pdf CURLOPT_ACCEPTTIMEOUT_MS.pdf \
|
|||||||
CURLMOPT_TIMERDATA.pdf CURLMOPT_TIMERFUNCTION.pdf \
|
CURLMOPT_TIMERDATA.pdf CURLMOPT_TIMERFUNCTION.pdf \
|
||||||
CURLOPT_UNIX_SOCKET_PATH.pdf CURLOPT_PATH_AS_IS.pdf \
|
CURLOPT_UNIX_SOCKET_PATH.pdf CURLOPT_PATH_AS_IS.pdf \
|
||||||
CURLOPT_PROXY_SERVICE_NAME.pdf CURLOPT_SERVICE_NAME.pdf \
|
CURLOPT_PROXY_SERVICE_NAME.pdf CURLOPT_SERVICE_NAME.pdf \
|
||||||
CURLOPT_PIPEWAIT.pdf
|
CURLOPT_PIPEWAIT.pdf CURLMOPT_PUSHDATA.pdf CURLMOPT_PUSHFUNCTION.pdf
|
||||||
|
|
||||||
CLEANFILES = $(HTMLPAGES) $(PDFPAGES)
|
CLEANFILES = $(HTMLPAGES) $(PDFPAGES)
|
||||||
|
|
||||||
|
@ -283,6 +283,30 @@ typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */
|
|||||||
void *userp); /* private callback
|
void *userp); /* private callback
|
||||||
pointer */
|
pointer */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Name: curl_push_callback
|
||||||
|
*
|
||||||
|
* Desc: This callback gets called when a new stream is being pushed by the
|
||||||
|
* server. It approves or denies the new stream.
|
||||||
|
*
|
||||||
|
* Returns: CURL_PUSH_OK or CURL_PUSH_DENY.
|
||||||
|
*/
|
||||||
|
#define CURL_PUSH_OK 0
|
||||||
|
#define CURL_PUSH_DENY 1
|
||||||
|
|
||||||
|
struct curl_pushheaders; /* forward declaration only */
|
||||||
|
|
||||||
|
CURL_EXTERN char *curl_pushheader_bynum(struct curl_pushheaders *h,
|
||||||
|
size_t num);
|
||||||
|
CURL_EXTERN char *curl_pushheader_byname(struct curl_pushheaders *h,
|
||||||
|
const char *name);
|
||||||
|
|
||||||
|
typedef int (*curl_push_callback)(CURL *parent,
|
||||||
|
CURL *easy,
|
||||||
|
size_t num_headers,
|
||||||
|
struct curl_pushheaders *headers,
|
||||||
|
void *userp);
|
||||||
|
|
||||||
CURL_EXTERN CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s,
|
CURL_EXTERN CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s,
|
||||||
int *running_handles);
|
int *running_handles);
|
||||||
|
|
||||||
@ -370,6 +394,12 @@ typedef enum {
|
|||||||
/* maximum number of open connections in total */
|
/* maximum number of open connections in total */
|
||||||
CINIT(MAX_TOTAL_CONNECTIONS, LONG, 13),
|
CINIT(MAX_TOTAL_CONNECTIONS, LONG, 13),
|
||||||
|
|
||||||
|
/* This is the server push callback function pointer */
|
||||||
|
CINIT(PUSHFUNCTION, FUNCTIONPOINT, 14),
|
||||||
|
|
||||||
|
/* This is the argument passed to the server push callback */
|
||||||
|
CINIT(PUSHDATA, OBJECTPOINT, 15),
|
||||||
|
|
||||||
CURLMOPT_LASTENTRY /* the last unused */
|
CURLMOPT_LASTENTRY /* the last unused */
|
||||||
} CURLMoption;
|
} CURLMoption;
|
||||||
|
|
||||||
|
@ -164,6 +164,7 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn)
|
|||||||
conn->data->req.protop = http;
|
conn->data->req.protop = http;
|
||||||
|
|
||||||
Curl_http2_setup_conn(conn);
|
Curl_http2_setup_conn(conn);
|
||||||
|
Curl_http2_setup_req(conn->data);
|
||||||
|
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
@ -175,6 +176,8 @@ static CURLcode http_disconnect(struct connectdata *conn, bool dead_connection)
|
|||||||
if(http) {
|
if(http) {
|
||||||
Curl_add_buffer_free(http->header_recvbuf);
|
Curl_add_buffer_free(http->header_recvbuf);
|
||||||
http->header_recvbuf = NULL; /* clear the pointer */
|
http->header_recvbuf = NULL; /* clear the pointer */
|
||||||
|
free(http->push_headers);
|
||||||
|
http->push_headers = NULL;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
(void)conn;
|
(void)conn;
|
||||||
@ -1491,6 +1494,8 @@ CURLcode Curl_http_done(struct connectdata *conn,
|
|||||||
DEBUGF(infof(data, "free header_recvbuf!!\n"));
|
DEBUGF(infof(data, "free header_recvbuf!!\n"));
|
||||||
Curl_add_buffer_free(http->header_recvbuf);
|
Curl_add_buffer_free(http->header_recvbuf);
|
||||||
http->header_recvbuf = NULL; /* clear the pointer */
|
http->header_recvbuf = NULL; /* clear the pointer */
|
||||||
|
free(http->push_headers);
|
||||||
|
http->push_headers = NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -176,6 +176,10 @@ struct HTTP {
|
|||||||
const uint8_t *upload_mem; /* points to a buffer to read from */
|
const uint8_t *upload_mem; /* points to a buffer to read from */
|
||||||
size_t upload_len; /* size of the buffer 'upload_mem' points to */
|
size_t upload_len; /* size of the buffer 'upload_mem' points to */
|
||||||
curl_off_t upload_left; /* number of bytes left to upload */
|
curl_off_t upload_left; /* number of bytes left to upload */
|
||||||
|
|
||||||
|
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
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
212
lib/http2.c
212
lib/http2.c
@ -33,6 +33,7 @@
|
|||||||
#include "rawstr.h"
|
#include "rawstr.h"
|
||||||
#include "multiif.h"
|
#include "multiif.h"
|
||||||
#include "conncache.h"
|
#include "conncache.h"
|
||||||
|
#include "url.h"
|
||||||
|
|
||||||
/* The last #include files should be: */
|
/* The last #include files should be: */
|
||||||
#include "curl_memory.h"
|
#include "curl_memory.h"
|
||||||
@ -94,12 +95,9 @@ static CURLcode http2_disconnect(struct connectdata *conn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* called from Curl_http_setup_conn */
|
/* called from Curl_http_setup_conn */
|
||||||
void Curl_http2_setup_conn(struct connectdata *conn)
|
void Curl_http2_setup_req(struct SessionHandle *data)
|
||||||
{
|
{
|
||||||
struct HTTP *http = conn->data->req.protop;
|
struct HTTP *http = data->req.protop;
|
||||||
|
|
||||||
conn->proto.httpc.settings.max_concurrent_streams =
|
|
||||||
DEFAULT_MAX_CONCURRENT_STREAMS;
|
|
||||||
|
|
||||||
http->nread_header_recvbuf = 0;
|
http->nread_header_recvbuf = 0;
|
||||||
http->bodystarted = FALSE;
|
http->bodystarted = FALSE;
|
||||||
@ -108,13 +106,18 @@ void Curl_http2_setup_conn(struct connectdata *conn)
|
|||||||
http->pauselen = 0;
|
http->pauselen = 0;
|
||||||
http->error_code = NGHTTP2_NO_ERROR;
|
http->error_code = NGHTTP2_NO_ERROR;
|
||||||
http->closed = FALSE;
|
http->closed = FALSE;
|
||||||
|
http->mem = data->state.buffer;
|
||||||
/* where to store incoming data for this stream and how big the buffer is */
|
|
||||||
http->mem = conn->data->state.buffer;
|
|
||||||
http->len = BUFSIZE;
|
http->len = BUFSIZE;
|
||||||
http->memlen = 0;
|
http->memlen = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* called from Curl_http_setup_conn */
|
||||||
|
void Curl_http2_setup_conn(struct connectdata *conn)
|
||||||
|
{
|
||||||
|
conn->proto.httpc.settings.max_concurrent_streams =
|
||||||
|
DEFAULT_MAX_CONCURRENT_STREAMS;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* HTTP2 handler interface. This isn't added to the general list of protocols
|
* HTTP2 handler interface. This isn't added to the general list of protocols
|
||||||
* but will be used at run-time when the protocol is dynamically switched from
|
* but will be used at run-time when the protocol is dynamically switched from
|
||||||
@ -205,6 +208,160 @@ static ssize_t send_callback(nghttp2_session *h2,
|
|||||||
return written;
|
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
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
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, const char *header)
|
||||||
|
{
|
||||||
|
/* Verify that we got a good easy handle in the push header struct,
|
||||||
|
mostly to detect rubbish input fast(er). Also empty header name
|
||||||
|
is just a rubbish too. We have to allow ":" at the beginning of
|
||||||
|
the header, but header == ":" must be rejected. If we have ':' in
|
||||||
|
the middle of header, it could be matched in middle of the value,
|
||||||
|
this is because we do prefix match.*/
|
||||||
|
if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
|
||||||
|
Curl_raw_equal(header, ":") || strchr(header + 1, ':'))
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CURL *duphandle(struct SessionHandle *data)
|
||||||
|
{
|
||||||
|
struct SessionHandle *second = curl_easy_duphandle(data);
|
||||||
|
if(second) {
|
||||||
|
/* setup the request struct */
|
||||||
|
struct HTTP *http = calloc(1, sizeof(struct HTTP));
|
||||||
|
if(!http) {
|
||||||
|
(void)Curl_close(second);
|
||||||
|
second = NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
second->req.protop = http;
|
||||||
|
http->header_recvbuf = Curl_add_buffer_init();
|
||||||
|
if(!http->header_recvbuf) {
|
||||||
|
free(http);
|
||||||
|
(void)Curl_close(second);
|
||||||
|
second = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Curl_http2_setup_req(second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return second;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int push_promise(struct SessionHandle *data,
|
||||||
|
struct connectdata *conn,
|
||||||
|
const nghttp2_push_promise *frame)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
DEBUGF(infof(data, "PUSH_PROMISE received, stream %u!\n",
|
||||||
|
frame->promised_stream_id));
|
||||||
|
if(data->multi->push_cb) {
|
||||||
|
struct HTTP *stream;
|
||||||
|
struct curl_pushheaders heads;
|
||||||
|
CURLMcode rc;
|
||||||
|
struct http_conn *httpc;
|
||||||
|
size_t i;
|
||||||
|
/* clone the parent */
|
||||||
|
CURL *newhandle = duphandle(data);
|
||||||
|
if(!newhandle) {
|
||||||
|
infof(data, "failed to duplicate handle\n");
|
||||||
|
rv = 1; /* FAIL HARD */
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
heads.data = data;
|
||||||
|
heads.frame = frame;
|
||||||
|
/* ask the application */
|
||||||
|
DEBUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
|
||||||
|
|
||||||
|
stream = data->req.protop;
|
||||||
|
|
||||||
|
rv = data->multi->push_cb(data, newhandle,
|
||||||
|
stream->push_headers_used, &heads,
|
||||||
|
data->multi->push_userp);
|
||||||
|
|
||||||
|
/* free the headers again */
|
||||||
|
for(i=0; i<stream->push_headers_used; i++)
|
||||||
|
free(stream->push_headers[i]);
|
||||||
|
free(stream->push_headers);
|
||||||
|
stream->push_headers = NULL;
|
||||||
|
|
||||||
|
if(rv) {
|
||||||
|
/* denied, kill off the new handle again */
|
||||||
|
(void)Curl_close(newhandle);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* approved, add to the multi handle and immediately switch to PERFORM
|
||||||
|
state with the given connection !*/
|
||||||
|
rc = Curl_multi_add_perform(data->multi, newhandle, conn);
|
||||||
|
if(rc) {
|
||||||
|
infof(data, "failed to add handle to multi\n");
|
||||||
|
Curl_close(newhandle);
|
||||||
|
rv = 1;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
httpc = &conn->proto.httpc;
|
||||||
|
/* put the newhandle in the hash with the stream id as key */
|
||||||
|
if(!Curl_hash_add(&httpc->streamsh,
|
||||||
|
(size_t *)&frame->promised_stream_id,
|
||||||
|
sizeof(frame->promised_stream_id), newhandle)) {
|
||||||
|
failf(conn->data, "Couldn't add stream to hash!");
|
||||||
|
rv = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
rv = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DEBUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
|
||||||
|
rv = 1;
|
||||||
|
}
|
||||||
|
fail:
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
|
static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
void *userp)
|
void *userp)
|
||||||
{
|
{
|
||||||
@ -292,13 +449,15 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
|
|||||||
Curl_expire(data_s, 1);
|
Curl_expire(data_s, 1);
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_PUSH_PROMISE:
|
case NGHTTP2_PUSH_PROMISE:
|
||||||
DEBUGF(infof(data_s, "Got PUSH_PROMISE, RST_STREAM it!\n"));
|
rv = push_promise(data_s, conn, &frame->push_promise);
|
||||||
|
if(rv) { /* deny! */
|
||||||
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
||||||
frame->push_promise.promised_stream_id,
|
frame->push_promise.promised_stream_id,
|
||||||
NGHTTP2_CANCEL);
|
NGHTTP2_CANCEL);
|
||||||
if(nghttp2_is_fatal(rv)) {
|
if(nghttp2_is_fatal(rv)) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_SETTINGS:
|
case NGHTTP2_SETTINGS:
|
||||||
{
|
{
|
||||||
@ -523,11 +682,6 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
|||||||
(void)frame;
|
(void)frame;
|
||||||
(void)flags;
|
(void)flags;
|
||||||
|
|
||||||
/* Ignore PUSH_PROMISE for now */
|
|
||||||
if(frame->hd.type != NGHTTP2_HEADERS) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
|
DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
|
||||||
|
|
||||||
/* get the stream from the hash based on Stream ID */
|
/* get the stream from the hash based on Stream ID */
|
||||||
@ -547,6 +701,36 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
|||||||
consequence is handled in on_frame_recv(). */
|
consequence is handled in on_frame_recv(). */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/* Store received PUSH_PROMISE headers to be used when the subsequent
|
||||||
|
PUSH_PROMISE callback comes */
|
||||||
|
if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
|
||||||
|
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 NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||||
|
}
|
||||||
|
stream->push_headers = headp;
|
||||||
|
}
|
||||||
|
h = aprintf("%s:%s", name, value);
|
||||||
|
if(h)
|
||||||
|
stream->push_headers[stream->push_headers_used++] = h;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if(namelen == sizeof(":status") - 1 &&
|
if(namelen == sizeof(":status") - 1 &&
|
||||||
memcmp(":status", name, namelen) == 0) {
|
memcmp(":status", name, namelen) == 0) {
|
||||||
/* nghttp2 guarantees :status is received first and only once, and
|
/* nghttp2 guarantees :status is received first and only once, and
|
||||||
|
@ -46,6 +46,7 @@ CURLcode Curl_http2_switched(struct connectdata *conn,
|
|||||||
const char *data, size_t nread);
|
const char *data, size_t nread);
|
||||||
/* called from Curl_http_setup_conn */
|
/* called from Curl_http_setup_conn */
|
||||||
void Curl_http2_setup_conn(struct connectdata *conn);
|
void Curl_http2_setup_conn(struct connectdata *conn);
|
||||||
|
void Curl_http2_setup_req(struct SessionHandle *data);
|
||||||
#else /* USE_NGHTTP2 */
|
#else /* USE_NGHTTP2 */
|
||||||
#define Curl_http2_init(x) CURLE_UNSUPPORTED_PROTOCOL
|
#define Curl_http2_init(x) CURLE_UNSUPPORTED_PROTOCOL
|
||||||
#define Curl_http2_send_request(x) CURLE_UNSUPPORTED_PROTOCOL
|
#define Curl_http2_send_request(x) CURLE_UNSUPPORTED_PROTOCOL
|
||||||
@ -53,6 +54,7 @@ void Curl_http2_setup_conn(struct connectdata *conn);
|
|||||||
#define Curl_http2_setup(x) CURLE_UNSUPPORTED_PROTOCOL
|
#define Curl_http2_setup(x) CURLE_UNSUPPORTED_PROTOCOL
|
||||||
#define Curl_http2_switched(x,y,z) CURLE_UNSUPPORTED_PROTOCOL
|
#define Curl_http2_switched(x,y,z) CURLE_UNSUPPORTED_PROTOCOL
|
||||||
#define Curl_http2_setup_conn(x)
|
#define Curl_http2_setup_conn(x)
|
||||||
|
#define Curl_http2_setup_req(x)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* HEADER_CURL_HTTP2_H */
|
#endif /* HEADER_CURL_HTTP2_H */
|
||||||
|
30
lib/multi.c
30
lib/multi.c
@ -62,8 +62,6 @@
|
|||||||
|
|
||||||
#define GOOD_MULTI_HANDLE(x) \
|
#define GOOD_MULTI_HANDLE(x) \
|
||||||
((x) && (((struct Curl_multi *)(x))->type == CURL_MULTI_HANDLE))
|
((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,
|
static void singlesocket(struct Curl_multi *multi,
|
||||||
struct SessionHandle *data);
|
struct SessionHandle *data);
|
||||||
@ -957,6 +955,28 @@ static bool multi_ischanged(struct Curl_multi *multi, bool clear)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
|
||||||
|
struct SessionHandle *data,
|
||||||
|
struct connectdata *conn)
|
||||||
|
{
|
||||||
|
CURLMcode rc;
|
||||||
|
|
||||||
|
rc = curl_multi_add_handle(multi, data);
|
||||||
|
if(!rc) {
|
||||||
|
struct SingleRequest *k = &data->req;
|
||||||
|
|
||||||
|
/* pass in NULL for 'conn' here since we don't want to init the
|
||||||
|
connection, only this transfer */
|
||||||
|
Curl_init_do(data, NULL);
|
||||||
|
|
||||||
|
/* take this handle to the perform state right away */
|
||||||
|
multistate(data, CURLM_STATE_PERFORM);
|
||||||
|
data->easy_conn = conn;
|
||||||
|
k->keepon |= KEEP_RECV; /* setup to receive! */
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
||||||
struct timeval now,
|
struct timeval now,
|
||||||
struct SessionHandle *data)
|
struct SessionHandle *data)
|
||||||
@ -2346,6 +2366,12 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle,
|
|||||||
case CURLMOPT_SOCKETDATA:
|
case CURLMOPT_SOCKETDATA:
|
||||||
multi->socket_userp = va_arg(param, void *);
|
multi->socket_userp = va_arg(param, void *);
|
||||||
break;
|
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:
|
case CURLMOPT_PIPELINING:
|
||||||
multi->pipelining = va_arg(param, long);
|
multi->pipelining = va_arg(param, long);
|
||||||
break;
|
break;
|
||||||
|
@ -87,6 +87,10 @@ struct Curl_multi {
|
|||||||
curl_socket_callback socket_cb;
|
curl_socket_callback socket_cb;
|
||||||
void *socket_userp;
|
void *socket_userp;
|
||||||
|
|
||||||
|
/* callback function and user data pointer for server push */
|
||||||
|
curl_push_callback push_cb;
|
||||||
|
void *push_userp;
|
||||||
|
|
||||||
/* Hostname cache */
|
/* Hostname cache */
|
||||||
struct curl_hash hostcache;
|
struct curl_hash hostcache;
|
||||||
|
|
||||||
|
@ -88,4 +88,10 @@ void Curl_multi_connchanged(struct Curl_multi *multi);
|
|||||||
|
|
||||||
void Curl_multi_closed(struct connectdata *conn, curl_socket_t s);
|
void Curl_multi_closed(struct connectdata *conn, curl_socket_t s);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a handle and move it into PERFORM state at once. For pushed streams.
|
||||||
|
*/
|
||||||
|
CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
|
||||||
|
struct SessionHandle *data,
|
||||||
|
struct connectdata *conn);
|
||||||
#endif /* HEADER_CURL_MULTIIF_H */
|
#endif /* HEADER_CURL_MULTIIF_H */
|
||||||
|
21
lib/url.c
21
lib/url.c
@ -142,7 +142,6 @@ find_oldest_idle_connection_in_bundle(struct SessionHandle *data,
|
|||||||
struct connectbundle *bundle);
|
struct connectbundle *bundle);
|
||||||
static void conn_free(struct connectdata *conn);
|
static void conn_free(struct connectdata *conn);
|
||||||
static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke);
|
static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke);
|
||||||
static CURLcode do_init(struct connectdata *conn);
|
|
||||||
static CURLcode parse_url_login(struct SessionHandle *data,
|
static CURLcode parse_url_login(struct SessionHandle *data,
|
||||||
struct connectdata *conn,
|
struct connectdata *conn,
|
||||||
char **userptr, char **passwdptr,
|
char **userptr, char **passwdptr,
|
||||||
@ -5651,7 +5650,7 @@ static CURLcode create_conn(struct SessionHandle *data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* since we skip do_init() */
|
/* since we skip do_init() */
|
||||||
do_init(conn);
|
Curl_init_do(data, conn);
|
||||||
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -5830,7 +5829,7 @@ static CURLcode create_conn(struct SessionHandle *data,
|
|||||||
conn->inuse = TRUE;
|
conn->inuse = TRUE;
|
||||||
|
|
||||||
/* Setup and init stuff before DO starts, in preparing for the transfer. */
|
/* Setup and init stuff before DO starts, in preparing for the transfer. */
|
||||||
do_init(conn);
|
Curl_init_do(data, conn);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Setup whatever necessary for a resumed transfer
|
* Setup whatever necessary for a resumed transfer
|
||||||
@ -6112,20 +6111,24 @@ CURLcode Curl_done(struct connectdata **connp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* do_init() inits the readwrite session. This is inited each time (in the DO
|
* Curl_init_do() inits the readwrite session. This is inited each time (in
|
||||||
* function before the protocol-specific DO functions are invoked) for a
|
* the DO function before the protocol-specific DO functions are invoked) for
|
||||||
* transfer, sometimes multiple times on the same SessionHandle. Make sure
|
* a transfer, sometimes multiple times on the same SessionHandle. Make sure
|
||||||
* nothing in here depends on stuff that are setup dynamically for the
|
* nothing in here depends on stuff that are setup dynamically for the
|
||||||
* transfer.
|
* transfer.
|
||||||
|
*
|
||||||
|
* Allow this function to get called with 'conn' set to NULL.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static CURLcode do_init(struct connectdata *conn)
|
CURLcode Curl_init_do(struct SessionHandle *data, struct connectdata *conn)
|
||||||
{
|
{
|
||||||
struct SessionHandle *data = conn->data;
|
|
||||||
struct SingleRequest *k = &data->req;
|
struct SingleRequest *k = &data->req;
|
||||||
|
|
||||||
|
if(conn)
|
||||||
|
conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to
|
||||||
|
* use */
|
||||||
|
|
||||||
data->state.done = FALSE; /* Curl_done() is not called yet */
|
data->state.done = FALSE; /* Curl_done() is not called yet */
|
||||||
conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to use */
|
|
||||||
data->state.expect100header = FALSE;
|
data->state.expect100header = FALSE;
|
||||||
|
|
||||||
if(data->set.opt_no_body)
|
if(data->set.opt_no_body)
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* | (__| |_| | _ <| |___
|
* | (__| |_| | _ <| |___
|
||||||
* \___|\___/|_| \_\_____|
|
* \___|\___/|_| \_\_____|
|
||||||
*
|
*
|
||||||
* Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
|
* Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
*
|
*
|
||||||
* This software is licensed as described in the file COPYING, which
|
* This software is licensed as described in the file COPYING, which
|
||||||
* you should have received as part of this distribution. The terms
|
* you should have received as part of this distribution. The terms
|
||||||
@ -27,6 +27,7 @@
|
|||||||
* Prototypes for library-wide functions provided by url.c
|
* Prototypes for library-wide functions provided by url.c
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
CURLcode Curl_init_do(struct SessionHandle *data, struct connectdata *conn);
|
||||||
CURLcode Curl_open(struct SessionHandle **curl);
|
CURLcode Curl_open(struct SessionHandle **curl);
|
||||||
CURLcode Curl_init_userdefined(struct UserDefined *set);
|
CURLcode Curl_init_userdefined(struct UserDefined *set);
|
||||||
CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
|
CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
|
||||||
|
@ -198,6 +198,8 @@
|
|||||||
#define HEADERSIZE 256
|
#define HEADERSIZE 256
|
||||||
|
|
||||||
#define CURLEASY_MAGIC_NUMBER 0xc0dedbadU
|
#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.
|
/* Some convenience macros to get the larger/smaller value out of two given.
|
||||||
We prefix with CURL to prevent name collisions. */
|
We prefix with CURL to prevent name collisions. */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user