]> granicus.if.org Git - curl/commitdiff
http2: added three stream prio/deps options
authorDaniel Stenberg <daniel@haxx.se>
Sun, 13 Sep 2015 14:07:05 +0000 (16:07 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 23 Oct 2015 06:22:38 +0000 (08:22 +0200)
CURLOPT_STREAM_DEPENDS

CURLOPT_STREAM_DEPENDS_E

CURLOPT_STREAM_PRIORITY

docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.3 [new file with mode: 0644]
docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.3 [new file with mode: 0644]
docs/libcurl/opts/CURLOPT_STREAM_PRIORITY.3 [new file with mode: 0644]
include/curl/curl.h
lib/http2.c
lib/http2.h
lib/url.c
lib/urldata.h

diff --git a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.3 b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.3
new file mode 100644 (file)
index 0000000..f3dcff3
--- /dev/null
@@ -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), "
diff --git a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.3 b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.3
new file mode 100644 (file)
index 0000000..1684c36
--- /dev/null
@@ -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), "
diff --git a/docs/libcurl/opts/CURLOPT_STREAM_PRIORITY.3 b/docs/libcurl/opts/CURLOPT_STREAM_PRIORITY.3
new file mode 100644 (file)
index 0000000..4b0f16a
--- /dev/null
@@ -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), "
index 2d482750da1a255fbd324d03470b60d64a659d62..5ea3d4ce958dd9dbe06adef714e4d2f96e1921f0 100644 (file)
@@ -1648,6 +1648,15 @@ typedef enum {
   /* Set the protocol used when curl is given a URL without a protocol */
   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 */
 } CURLoption;
 
index 0cbb623c41d4a8dc72402809a9eb4b33ac93bb4a..9768c97da0edd6e6461f7a9d0fdca2ad14d19642 100644 (file)
 #error too old nghttp2 version, upgrade!
 #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,
                                  curl_socket_t *sock, /* points to
                                                          numsocks
@@ -986,6 +1004,54 @@ static ssize_t http2_handle_stream_close(struct http_conn *httpc,
   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
  * 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
        nghttp2_session_mem_recv() may queue new frame */
-    rv = nghttp2_session_send(httpc->h2);
+    rv = h2_session_send(data, httpc->h2);
     if(rv != 0) {
       *err = CURLE_SEND_ERROR;
       return 0;
@@ -1199,6 +1265,7 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
   nghttp2_data_provider data_prd;
   int32_t stream_id;
   nghttp2_session *h2 = httpc->h2;
+  nghttp2_priority_spec pri_spec;
 
   (void)sockindex;
 
@@ -1210,7 +1277,7 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
     stream->upload_mem = mem;
     stream->upload_len = len;
     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)) {
       *err = CURLE_SEND_ERROR;
       return -1;
@@ -1351,17 +1418,19 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
     nva[i] = authority;
   }
 
+  h2_pri_spec(conn->data, &pri_spec);
+
   switch(conn->data->set.httpreq) {
   case HTTPREQ_POST:
   case HTTPREQ_POST_FORM:
   case HTTPREQ_PUT:
     data_prd.read_callback = data_source_read_callback;
     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);
     break;
   default:
-    stream_id = nghttp2_submit_request(h2, NULL, nva, nheader,
+    stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
                                        NULL, conn->data);
   }
 
@@ -1377,6 +1446,8 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
         stream_id, conn->data);
   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);
 
   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. */
-  rv = nghttp2_session_send(httpc->h2);
+  rv = h2_session_send(data, httpc->h2);
 
   if(rv != 0) {
     failf(data, "nghttp2_session_send() failed: %s(%d)",
index bb7ad9c4ccfc3c9f51f94a73b150d996a81f6670..b9743806cbacdf88cd7a507772dfbef372311bfe 100644 (file)
@@ -38,6 +38,8 @@
 int Curl_http2_ver(char *p, size_t len);
 
 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_request_upgrade(Curl_send_buffer *req,
                                     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_setup_conn(x)
 #define Curl_http2_setup_req(x)
+#define Curl_http2_init_state(x)
+#define Curl_http2_init_userset(x)
 #endif
 
 #endif /* HEADER_CURL_HTTP2_H */
index ed86596758dd37b35f69f580ff20184f2de20ff9..866fee2eb196a126de31e17ed514a4a715eada0d 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -111,6 +111,7 @@ int curl_win32_idn_to_ascii(const char *in, char **out);
 #include "telnet.h"
 #include "tftp.h"
 #include "http.h"
+#include "http2.h"
 #include "file.h"
 #include "curl_ldap.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->sep_headers = TRUE; /* separated header lists by default */
+
+  Curl_http2_init_userset(set);
   return result;
 }
 
@@ -673,6 +676,8 @@ CURLcode Curl_open(struct SessionHandle **curl)
     data->wildcard.filelist = NULL;
     data->set.fnmatch = ZERO_NULL;
     data->set.maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */
+
+    Curl_http2_init_state(&data->state);
   }
 
   if(result) {
@@ -2658,6 +2663,29 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
   case CURLOPT_PIPEWAIT:
     data->set.pipewait = (0 != va_arg(param, long))?TRUE:FALSE;
     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:
     /* unknown tag and its companion, just ignore: */
     result = CURLE_UNKNOWN_OPTION;
index b467e503eea538d1124d136aff86d41ae250a6d9..7d6025cb512e2cc68228e349ec3e7ea49455b8ed 100644 (file)
@@ -1337,6 +1337,10 @@ struct UrlState {
 
   curl_read_callback fread_func; /* read callback/function */
   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
                            new connection */
   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 {