1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-22 08:08:50 -05:00

http2: added three stream prio/deps options

CURLOPT_STREAM_DEPENDS

CURLOPT_STREAM_DEPENDS_E

CURLOPT_STREAM_PRIORITY
This commit is contained in:
Daniel Stenberg 2015-09-13 16:07:05 +02:00
parent 23cc0c00d4
commit 3042cb5043
8 changed files with 296 additions and 5 deletions

View File

@ -0,0 +1,56 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * 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 CURLOPT_STREAM_DEPENDS 3 "13 Sep 2015" "libcurl 7.46.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_STREAM_DEPENDS \- set stream this transfer depends on
.SH SYNOPSIS
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_STREAM_DEPENDS, CURL *dephandle);
.SH DESCRIPTION
Pass a CURL * pointer in \fIdephandle\fP to identify the stream within the
same connection that this stream is depending upon. This option clears the
exclusive it and is mutually exclusive to the
\fICURLOPT_STREAM_DEPENDS_E(3)\fP option.
The spec says "Including a dependency expresses a preference to allocate
resources to the identified stream rather than to the dependent stream."
This option can be set during transfer.
\fIdephandle\fP must not be the same as \fIhandle\fP, that will cause this
function to return an error. It must be another easy handle, and it also needs
to be a handle of a transfer that will be sent over the same HTTP/2 connection
for this option to have an actual effect.
.SH DEFAULT
NULL
.SH PROTOCOLS
HTTP/2
.SH EXAMPLE
TODO
.SH AVAILABILITY
Added in 7.46.0
.SH RETURN VALUE
Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
.SH "SEE ALSO"
.BR CURLOPT_STREAM_PRIORITY "(3), " CURLOPT_STREAM_DEPENDS_E "(3), "

View File

@ -0,0 +1,59 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * 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 CURLOPT_STREAM_DEPENDS_E 3 "13 Sep 2015" "libcurl 7.46.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_STREAM_DEPENDS_E \- set stream this transfer depends on execlusively
.SH SYNOPSIS
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_STREAM_DEPENDS_E, CURL *dephandle);
.SH DESCRIPTION
Pass a CURL * pointer in \fIdephandle\fP to identify the stream within the
same connection that this stream is depending upon exclusively. That means it
depends on it and sets the Exclusive bit.
The spec says "Including a dependency expresses a preference to allocate
resources to the identified stream rather than to the dependent stream."
Setting a dependency with the exclusive flag for a reprioritized stream causes
all the dependencies of the new parent stream to become dependent on the
reprioritized stream.
This option can be set during transfer.
\fIdephandle\fP must not be the same as \fIhandle\fP, that will cause this
function to return an error. It must be another easy handle, and it also needs
to be a handle of a transfer that will be sent over the same HTTP/2 connection
for this option to have an actual effect.
.SH DEFAULT
NULL
.SH PROTOCOLS
HTTP/2
.SH EXAMPLE
TODO
.SH AVAILABILITY
Added in 7.46.0
.SH RETURN VALUE
Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
.SH "SEE ALSO"
.BR CURLOPT_STREAM_PRIORITY "(3), " CURLOPT_STREAM_DEPENDS "(3), "

View File

@ -0,0 +1,56 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * 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 CURLOPT_STREAM_PRIORITY 3 "13 Sep 2015" "libcurl 7.46.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_STREAM_PRIORITY \- set numerical stream priority
.SH SYNOPSIS
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_STREAM_PRIORITY, long prio);
.SH DESCRIPTION
Set the long \fIprio\fP to a number between 1 and 256.
For protocols with priorities on streams, such as HTTP/2, this option can be
used to set an individual priority for this particular stream used by the easy
\fIhandle\fP. Setting and using priorities really only makes sense and is only
usable when doing multiple streams over the same connections, which thus
implies that you use \fICURLMOPT_PIPELINING(3)\fP.
This option can be set during transfer.
See section 5.3 of RFC 7540 for protocol details:
https://httpwg.github.io/specs/rfc7540.html#StreamPriority
.SH DEFAULT
If nothing is set, the HTTP/2 protocol itself will use its own default which
is 16.
.SH PROTOCOLS
HTTP/2
.SH EXAMPLE
TODO
.SH AVAILABILITY
Added in 7.46.0
.SH RETURN VALUE
Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
.SH "SEE ALSO"
.BR CURLOPT_STREAM_DEPENDS "(3), " CURLOPT_STREAM_DEPENDS_E "(3), "
.BR CURLOPT_PIPEWAIT "(3), " CURLMOPT_PIPELINING "(3), "

View File

@ -1648,6 +1648,15 @@ typedef enum {
/* Set the protocol used when curl is given a URL without a protocol */ /* Set the protocol used when curl is given a URL without a protocol */
CINIT(DEFAULT_PROTOCOL, OBJECTPOINT, 238), CINIT(DEFAULT_PROTOCOL, OBJECTPOINT, 238),
/* Set stream priority, 1 - 256 */
CINIT(STREAM_PRIORITY, LONG, 239),
/* Set stream dependency on another CURL handle */
CINIT(STREAM_DEPENDS, OBJECTPOINT, 240),
/* Set E-xclusive stream dependency on another CURL handle */
CINIT(STREAM_DEPENDS_E, OBJECTPOINT, 241),
CURLOPT_LASTENTRY /* the last unused */ CURLOPT_LASTENTRY /* the last unused */
} CURLoption; } CURLoption;

View File

@ -46,6 +46,24 @@
#error too old nghttp2 version, upgrade! #error too old nghttp2 version, upgrade!
#endif #endif
/*
* Curl_http2_init_state() is called when the easy handle is created and
* allows for HTTP/2 specific init of state.
*/
void Curl_http2_init_state(struct UrlState *state)
{
state->stream_prio = NGHTTP2_DEFAULT_WEIGHT;
}
/*
* Curl_http2_init_userset() is called when the easy handle is created and
* allows for HTTP/2 specific user-set fields.
*/
void Curl_http2_init_userset(struct UserDefined *set)
{
set->stream_prio = NGHTTP2_DEFAULT_WEIGHT;
}
static int http2_perform_getsock(const struct connectdata *conn, static int http2_perform_getsock(const struct connectdata *conn,
curl_socket_t *sock, /* points to curl_socket_t *sock, /* points to
numsocks numsocks
@ -986,6 +1004,54 @@ static ssize_t http2_handle_stream_close(struct http_conn *httpc,
return 0; return 0;
} }
/*
* h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
* and dependency to the peer. It also stores the updated values in the state
* struct.
*/
static void h2_pri_spec(struct SessionHandle *data,
nghttp2_priority_spec *pri_spec)
{
struct HTTP *depstream = (data->set.stream_depends_on?
data->set.stream_depends_on->req.protop:NULL);
int32_t depstream_id = depstream? depstream->stream_id:0;
nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_prio,
data->set.stream_depends_e);
data->state.stream_prio = data->set.stream_prio;
data->state.stream_depends_e = data->set.stream_depends_e;
data->state.stream_depends_on = data->set.stream_depends_on;
}
/*
* h2_session_send() checks if there's been an update in the priority /
* dependency settings and if so it submits a PRIORITY frame with the updated
* info.
*/
static int h2_session_send(struct SessionHandle *data,
nghttp2_session *h2)
{
struct HTTP *stream = data->req.protop;
if((data->set.stream_prio != data->state.stream_prio) ||
(data->set.stream_depends_e != data->state.stream_depends_e) ||
(data->set.stream_depends_on != data->state.stream_depends_on) ) {
/* send new weight and/or dependency */
nghttp2_priority_spec pri_spec;
int rv;
h2_pri_spec(data, &pri_spec);
DEBUGF(infof(data, "Queuing HTTP/2 PRIORITY frame on stream %u!\n",
stream->stream_id));
rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
&pri_spec);
if(rv)
return rv;
}
return nghttp2_session_send(h2);
}
/* /*
* If the read would block (EWOULDBLOCK) we return -1. Otherwise we return * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
* a regular CURLcode value. * a regular CURLcode value.
@ -1140,7 +1206,7 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
} }
/* Always send pending frames in nghttp2 session, because /* Always send pending frames in nghttp2 session, because
nghttp2_session_mem_recv() may queue new frame */ nghttp2_session_mem_recv() may queue new frame */
rv = nghttp2_session_send(httpc->h2); rv = h2_session_send(data, httpc->h2);
if(rv != 0) { if(rv != 0) {
*err = CURLE_SEND_ERROR; *err = CURLE_SEND_ERROR;
return 0; return 0;
@ -1199,6 +1265,7 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
nghttp2_data_provider data_prd; nghttp2_data_provider data_prd;
int32_t stream_id; int32_t stream_id;
nghttp2_session *h2 = httpc->h2; nghttp2_session *h2 = httpc->h2;
nghttp2_priority_spec pri_spec;
(void)sockindex; (void)sockindex;
@ -1210,7 +1277,7 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
stream->upload_mem = mem; stream->upload_mem = mem;
stream->upload_len = len; stream->upload_len = len;
nghttp2_session_resume_data(h2, stream->stream_id); nghttp2_session_resume_data(h2, stream->stream_id);
rv = nghttp2_session_send(h2); rv = h2_session_send(conn->data, h2);
if(nghttp2_is_fatal(rv)) { if(nghttp2_is_fatal(rv)) {
*err = CURLE_SEND_ERROR; *err = CURLE_SEND_ERROR;
return -1; return -1;
@ -1351,17 +1418,19 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
nva[i] = authority; nva[i] = authority;
} }
h2_pri_spec(conn->data, &pri_spec);
switch(conn->data->set.httpreq) { switch(conn->data->set.httpreq) {
case HTTPREQ_POST: case HTTPREQ_POST:
case HTTPREQ_POST_FORM: case HTTPREQ_POST_FORM:
case HTTPREQ_PUT: case HTTPREQ_PUT:
data_prd.read_callback = data_source_read_callback; data_prd.read_callback = data_source_read_callback;
data_prd.source.ptr = NULL; data_prd.source.ptr = NULL;
stream_id = nghttp2_submit_request(h2, NULL, nva, nheader, stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
&data_prd, conn->data); &data_prd, conn->data);
break; break;
default: default:
stream_id = nghttp2_submit_request(h2, NULL, nva, nheader, stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
NULL, conn->data); NULL, conn->data);
} }
@ -1377,6 +1446,8 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
stream_id, conn->data); stream_id, conn->data);
stream->stream_id = stream_id; stream->stream_id = stream_id;
/* this does not call h2_session_send() since there can not have been any
* priority upodate since the nghttp2_submit_request() call above */
rv = nghttp2_session_send(h2); rv = nghttp2_session_send(h2);
if(rv != 0) { if(rv != 0) {
@ -1536,7 +1607,7 @@ CURLcode Curl_http2_switched(struct connectdata *conn,
} }
/* Try to send some frames since we may read SETTINGS already. */ /* Try to send some frames since we may read SETTINGS already. */
rv = nghttp2_session_send(httpc->h2); rv = h2_session_send(data, httpc->h2);
if(rv != 0) { if(rv != 0) {
failf(data, "nghttp2_session_send() failed: %s(%d)", failf(data, "nghttp2_session_send() failed: %s(%d)",

View File

@ -38,6 +38,8 @@
int Curl_http2_ver(char *p, size_t len); int Curl_http2_ver(char *p, size_t len);
CURLcode Curl_http2_init(struct connectdata *conn); CURLcode Curl_http2_init(struct connectdata *conn);
void Curl_http2_init_state(struct UrlState *state);
void Curl_http2_init_userset(struct UserDefined *set);
CURLcode Curl_http2_send_request(struct connectdata *conn); CURLcode Curl_http2_send_request(struct connectdata *conn);
CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req, CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
struct connectdata *conn); struct connectdata *conn);
@ -55,6 +57,8 @@ void Curl_http2_setup_req(struct SessionHandle *data);
#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) #define Curl_http2_setup_req(x)
#define Curl_http2_init_state(x)
#define Curl_http2_init_userset(x)
#endif #endif
#endif /* HEADER_CURL_HTTP2_H */ #endif /* HEADER_CURL_HTTP2_H */

View File

@ -111,6 +111,7 @@ int curl_win32_idn_to_ascii(const char *in, char **out);
#include "telnet.h" #include "telnet.h"
#include "tftp.h" #include "tftp.h"
#include "http.h" #include "http.h"
#include "http2.h"
#include "file.h" #include "file.h"
#include "curl_ldap.h" #include "curl_ldap.h"
#include "ssh.h" #include "ssh.h"
@ -616,6 +617,8 @@ CURLcode Curl_init_userdefined(struct UserDefined *set)
set->expect_100_timeout = 1000L; /* Wait for a second by default. */ set->expect_100_timeout = 1000L; /* Wait for a second by default. */
set->sep_headers = TRUE; /* separated header lists by default */ set->sep_headers = TRUE; /* separated header lists by default */
Curl_http2_init_userset(set);
return result; return result;
} }
@ -673,6 +676,8 @@ CURLcode Curl_open(struct SessionHandle **curl)
data->wildcard.filelist = NULL; data->wildcard.filelist = NULL;
data->set.fnmatch = ZERO_NULL; data->set.fnmatch = ZERO_NULL;
data->set.maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ data->set.maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */
Curl_http2_init_state(&data->state);
} }
if(result) { if(result) {
@ -2658,6 +2663,29 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
case CURLOPT_PIPEWAIT: case CURLOPT_PIPEWAIT:
data->set.pipewait = (0 != va_arg(param, long))?TRUE:FALSE; data->set.pipewait = (0 != va_arg(param, long))?TRUE:FALSE;
break; break;
case CURLOPT_STREAM_PRIORITY:
#ifndef USE_NGHTTP2
return CURLE_NOT_BUILT_IN;
#else
arg = va_arg(param, long);
if((arg>=1) && (arg <= 256))
data->set.stream_prio = (int)arg;
break;
#endif
case CURLOPT_STREAM_DEPENDS:
case CURLOPT_STREAM_DEPENDS_E:
{
#ifndef USE_NGHTTP2
return CURLE_NOT_BUILT_IN;
#else
struct SessionHandle *dep = va_arg(param, struct SessionHandle *);
if(dep && GOOD_EASY_HANDLE(dep)) {
data->set.stream_depends_on = dep;
data->set.stream_depends_e = (option == CURLOPT_STREAM_DEPENDS_E);
}
break;
#endif
}
default: default:
/* unknown tag and its companion, just ignore: */ /* unknown tag and its companion, just ignore: */
result = CURLE_UNKNOWN_OPTION; result = CURLE_UNKNOWN_OPTION;

View File

@ -1337,6 +1337,10 @@ struct UrlState {
curl_read_callback fread_func; /* read callback/function */ curl_read_callback fread_func; /* read callback/function */
void *in; /* CURLOPT_READDATA */ void *in; /* CURLOPT_READDATA */
struct SessionHandle *stream_depends_on;
bool stream_depends_e; /* set or don't set the Exclusive bit */
int stream_prio;
}; };
@ -1653,6 +1657,10 @@ struct UserDefined {
bool pipewait; /* wait for pipe/multiplex status before starting a bool pipewait; /* wait for pipe/multiplex status before starting a
new connection */ new connection */
long expect_100_timeout; /* in milliseconds */ long expect_100_timeout; /* in milliseconds */
struct SessionHandle *stream_depends_on;
bool stream_depends_e; /* set or don't set the Exclusive bit */
int stream_prio;
}; };
struct Names { struct Names {