From bfbff7852f050232edd3e5ca5c6bf2021c340f5a Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 22 May 2026 09:11:41 +0200 Subject: [PATCH] http2: remove stream dependency tracking The HTTP/2 feature is deprecated, few servers implement it and our implementation is complicated by its state management. Make the two CURLOPT_* involved a nop and deprecate them. Closes #21723 --- docs/libcurl/curl_easy_setopt.md | 5 +- docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md | 8 ++ docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md | 8 ++ docs/libcurl/symbols-in-versions | 4 +- include/curl/curl.h | 6 +- lib/http2.c | 18 ++-- lib/setopt.c | 9 +- lib/url.c | 98 ------------------- lib/urldata.h | 8 -- 9 files changed, 34 insertions(+), 130 deletions(-) diff --git a/docs/libcurl/curl_easy_setopt.md b/docs/libcurl/curl_easy_setopt.md index 37d028954e..5f75596cff 100644 --- a/docs/libcurl/curl_easy_setopt.md +++ b/docs/libcurl/curl_easy_setopt.md @@ -1165,11 +1165,12 @@ Redirect stderr to another stream. See CURLOPT_STDERR(3) ## CURLOPT_STREAM_DEPENDS -This HTTP/2 stream depends on another. See CURLOPT_STREAM_DEPENDS(3) +**Deprecated option** This HTTP/2 stream depends on another. See +CURLOPT_STREAM_DEPENDS(3) ## CURLOPT_STREAM_DEPENDS_E -This HTTP/2 stream depends on another exclusively. See +**Deprecated option** This HTTP/2 stream depends on another exclusively. See CURLOPT_STREAM_DEPENDS_E(3) ## CURLOPT_STREAM_WEIGHT diff --git a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md index 5e3e177dce..e0e8ef0c53 100644 --- a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md +++ b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md @@ -44,6 +44,10 @@ an error. It must be another easy handle, and it also needs to be a handle of a transfer that is about to be sent over the same HTTP/2 connection for this option to have an actual effect. +Since version 8.21.0 setting this option no longer has an effect. HTTP/2 +stream dependencies were introduced in RFC 7540 and then later deprecated +in RFC 9113. + # DEFAULT NULL @@ -69,6 +73,10 @@ int main(void) } ~~~ +# DEPRECATED + +Deprecated since 8.21.0. + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md index fe95ae8d71..fe8f97f950 100644 --- a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md +++ b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md @@ -47,6 +47,10 @@ an error. It must be another easy handle, and it also needs to be a handle of a transfer that is about to be sent over the same HTTP/2 connection for this option to have an actual effect. +Since version 8.21.0 setting this option no longer has an effect. HTTP/2 +stream dependencies were introduced in RFC 7540 and then later deprecated +in RFC 9113. + # DEFAULT NULL @@ -72,6 +76,10 @@ int main(void) } ~~~ +# DEPRECATED + +Deprecated since 8.21.0. + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 6516f7823d..4dc670da6e 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -882,8 +882,8 @@ CURLOPT_SSLKEYPASSWD 7.9.3 7.17.0 CURLOPT_SSLKEYTYPE 7.9.3 CURLOPT_SSLVERSION 7.1 CURLOPT_STDERR 7.1 -CURLOPT_STREAM_DEPENDS 7.46.0 -CURLOPT_STREAM_DEPENDS_E 7.46.0 +CURLOPT_STREAM_DEPENDS 7.46.0 8.21.0 +CURLOPT_STREAM_DEPENDS_E 7.46.0 8.21.0 CURLOPT_STREAM_WEIGHT 7.46.0 CURLOPT_SUPPRESS_CONNECT_HEADERS 7.54.0 CURLOPT_TCP_FASTOPEN 7.49.0 diff --git a/include/curl/curl.h b/include/curl/curl.h index 8009df4051..cb36eefad4 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -1985,10 +1985,12 @@ typedef enum { CURLOPT(CURLOPT_STREAM_WEIGHT, CURLOPTTYPE_LONG, 239), /* Set stream dependency on another curl handle */ - CURLOPT(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240), + CURLOPTDEPRECATED(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240, + 8.21.0, "Has no function"), /* Set E-xclusive stream dependency on another curl handle */ - CURLOPT(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241), + CURLOPTDEPRECATED(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241, + 8.21.0, "Has no function"), /* Do not send any tftp option requests to the server */ CURLOPT(CURLOPT_TFTP_NO_OPTIONS, CURLOPTTYPE_LONG, 242), diff --git a/lib/http2.c b/lib/http2.c index c8ecb28b5a..9eb1e0aeaa 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -1778,16 +1778,12 @@ static int sweight_in_effect(const struct Curl_easy *data) * struct. */ -static void h2_pri_spec(struct cf_h2_ctx *ctx, - struct Curl_easy *data, +static void h2_pri_spec(struct Curl_easy *data, nghttp2_priority_spec *pri_spec) { struct Curl_data_priority *prio = &data->set.priority; - struct h2_stream_ctx *depstream = H2_STREAM_CTX(ctx, prio->parent); - int32_t depstream_id = depstream ? depstream->id : 0; - nghttp2_priority_spec_init(pri_spec, depstream_id, - sweight_wanted(data), - data->set.priority.exclusive); + nghttp2_priority_spec_init(pri_spec, 0, + sweight_wanted(data), FALSE); data->state.priority = *prio; } @@ -1805,13 +1801,11 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf, int rv = 0; if(stream && stream->id > 0 && - ((sweight_wanted(data) != sweight_in_effect(data)) || - (data->set.priority.exclusive != data->state.priority.exclusive) || - (data->set.priority.parent != data->state.priority.parent))) { + (sweight_wanted(data) != sweight_in_effect(data))) { /* send new weight and/or dependency */ nghttp2_priority_spec pri_spec; - h2_pri_spec(ctx, data, &pri_spec); + h2_pri_spec(data, &pri_spec); CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id); DEBUGASSERT(stream->id != -1); rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE, @@ -2123,7 +2117,7 @@ static CURLcode h2_submit(struct h2_stream_ctx **pstream, goto out; } - h2_pri_spec(ctx, data, &pri_spec); + h2_pri_spec(data, &pri_spec); if(!nghttp2_session_check_request_allowed(ctx->h2)) CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)"); diff --git a/lib/setopt.c b/lib/setopt.c index 067a8450de..2e08a310eb 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1527,13 +1527,10 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, #ifdef USE_HTTP2 case CURLOPT_STREAM_DEPENDS: - case CURLOPT_STREAM_DEPENDS_E: { - struct Curl_easy *dep = va_arg(param, struct Curl_easy *); - if(!dep || GOOD_EASY_HANDLE(dep)) - return Curl_data_priority_add_child(dep, data, - option == CURLOPT_STREAM_DEPENDS_E); + case CURLOPT_STREAM_DEPENDS_E: + /* not doing stream dependencies any longer, but accept options + * for backward compatibility */ break; - } #endif default: diff --git a/lib/url.c b/lib/url.c index 354505ad6c..796d35e229 100644 --- a/lib/url.c +++ b/lib/url.c @@ -119,12 +119,6 @@ #include "smtp.h" #include "ws.h" -#ifdef USE_NGHTTP2 -static void data_priority_cleanup(struct Curl_easy *data); -#else -#define data_priority_cleanup(x) -#endif - /* Some parts of the code (e.g. chunked encoding) assume this buffer has more * than a few bytes to play with. Do not let it become too small or bad things * will happen. @@ -275,8 +269,6 @@ CURLcode Curl_close(struct Curl_easy **datap) curlx_safefree(data->info.contenttype); curlx_safefree(data->info.wouldredirect); - data_priority_cleanup(data); - /* No longer a dirty share, if it exists */ if(Curl_share_easy_unlink(data)) DEBUGASSERT(0); @@ -3009,96 +3001,6 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) #if defined(USE_HTTP2) || defined(USE_HTTP3) -#ifdef USE_NGHTTP2 - -static void priority_remove_child(struct Curl_easy *parent, - struct Curl_easy *child) -{ - struct Curl_data_prio_node **pnext = &parent->set.priority.children; - struct Curl_data_prio_node *pnode = parent->set.priority.children; - - DEBUGASSERT(child->set.priority.parent == parent); - while(pnode && pnode->data != child) { - pnext = &pnode->next; - pnode = pnode->next; - } - - DEBUGASSERT(pnode); - if(pnode) { - *pnext = pnode->next; - curlx_free(pnode); - } - - child->set.priority.parent = 0; - child->set.priority.exclusive = FALSE; -} - -CURLcode Curl_data_priority_add_child(struct Curl_easy *parent, - struct Curl_easy *child, - bool exclusive) -{ - if(child->set.priority.parent) { - priority_remove_child(child->set.priority.parent, child); - } - - if(parent) { - struct Curl_data_prio_node **tail; - struct Curl_data_prio_node *pnode; - - pnode = curlx_calloc(1, sizeof(*pnode)); - if(!pnode) - return CURLE_OUT_OF_MEMORY; - pnode->data = child; - - if(parent->set.priority.children && exclusive) { - /* exclusive: move all existing children underneath the new child */ - struct Curl_data_prio_node *node = parent->set.priority.children; - while(node) { - node->data->set.priority.parent = child; - node = node->next; - } - - tail = &child->set.priority.children; - while(*tail) - tail = &(*tail)->next; - - DEBUGASSERT(!*tail); - *tail = parent->set.priority.children; - parent->set.priority.children = 0; - } - - tail = &parent->set.priority.children; - while(*tail) { - (*tail)->data->set.priority.exclusive = FALSE; - tail = &(*tail)->next; - } - - DEBUGASSERT(!*tail); - *tail = pnode; - } - - child->set.priority.parent = parent; - child->set.priority.exclusive = exclusive; - return CURLE_OK; -} - -#endif /* USE_NGHTTP2 */ - -#ifdef USE_NGHTTP2 -static void data_priority_cleanup(struct Curl_easy *data) -{ - while(data->set.priority.children) { - struct Curl_easy *tmp = data->set.priority.children->data; - priority_remove_child(data, tmp); - if(data->set.priority.parent) - Curl_data_priority_add_child(data->set.priority.parent, tmp, FALSE); - } - - if(data->set.priority.parent) - priority_remove_child(data->set.priority.parent, data); -} -#endif - void Curl_data_priority_clear_state(struct Curl_easy *data) { memset(&data->state.priority, 0, sizeof(data->state.priority)); diff --git a/lib/urldata.h b/lib/urldata.h index 63d231dc5c..4ee5108b17 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -592,15 +592,7 @@ struct Curl_data_prio_node { * on the same connection. */ struct Curl_data_priority { -#ifdef USE_NGHTTP2 - /* tree like dependencies only implemented in nghttp2 */ - struct Curl_easy *parent; - struct Curl_data_prio_node *children; -#endif int weight; -#ifdef USE_NGHTTP2 - BIT(exclusive); -#endif }; /* Timers */