mirror of
https://github.com/curl/curl.git
synced 2025-09-16 09:02:40 +03:00
http2: aggregate small SETTINGS/PRIO/WIN_UPDATE frames
add a small buffer to nghttp2 session sending in order to aggregate small SETTINGS/PRIO/WIN_UPDATE frames that nghttp2 "writes" to the callback individually. Ref: #10389 Closes #10432
This commit is contained in:
parent
e8b00fcd6a
commit
ead2b2d4f6
|
@ -920,7 +920,6 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
|
||||||
DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
|
DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
|
||||||
ctx->started_at = Curl_now();
|
ctx->started_at = Curl_now();
|
||||||
result = socket_open(data, &ctx->addr, &ctx->sock);
|
result = socket_open(data, &ctx->addr, &ctx->sock);
|
||||||
DEBUGF(LOG_CF(data, cf, "socket_open() -> %d, fd=%d", result, ctx->sock));
|
|
||||||
if(result)
|
if(result)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -1068,7 +1067,6 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
|
||||||
CURLcode result = CURLE_COULDNT_CONNECT;
|
CURLcode result = CURLE_COULDNT_CONNECT;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
DEBUGF(LOG_CF(data, cf, "connect"));
|
|
||||||
(void)data;
|
(void)data;
|
||||||
if(cf->connected) {
|
if(cf->connected) {
|
||||||
*done = TRUE;
|
*done = TRUE;
|
||||||
|
@ -1086,7 +1084,6 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
|
||||||
if(result)
|
if(result)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
DEBUGF(LOG_CF(data, cf, "connect opened(%d)", (int)ctx->sock));
|
|
||||||
/* Connect TCP socket */
|
/* Connect TCP socket */
|
||||||
rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen);
|
rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen);
|
||||||
if(-1 == rc) {
|
if(-1 == rc) {
|
||||||
|
@ -1106,6 +1103,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
|
||||||
rc = SOCKET_WRITABLE(ctx->sock, 0);
|
rc = SOCKET_WRITABLE(ctx->sock, 0);
|
||||||
|
|
||||||
if(rc == 0) { /* no connection yet */
|
if(rc == 0) { /* no connection yet */
|
||||||
|
DEBUGF(LOG_CF(data, cf, "not connected yet"));
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
|
else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
|
||||||
|
@ -1115,6 +1113,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
|
||||||
set_local_ip(cf, data);
|
set_local_ip(cf, data);
|
||||||
*done = TRUE;
|
*done = TRUE;
|
||||||
cf->connected = TRUE;
|
cf->connected = TRUE;
|
||||||
|
DEBUGF(LOG_CF(data, cf, "connected"));
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
100
lib/http2.c
100
lib/http2.c
|
@ -106,6 +106,8 @@ struct cf_h2_ctx {
|
||||||
size_t inbuflen; /* number of bytes filled in inbuf */
|
size_t inbuflen; /* number of bytes filled in inbuf */
|
||||||
size_t nread_inbuf; /* number of bytes read from in inbuf */
|
size_t nread_inbuf; /* number of bytes read from in inbuf */
|
||||||
|
|
||||||
|
struct dynbuf outbuf;
|
||||||
|
|
||||||
/* We need separate buffer for transmission and reception because we
|
/* We need separate buffer for transmission and reception because we
|
||||||
may call nghttp2_session_send() after the
|
may call nghttp2_session_send() after the
|
||||||
nghttp2_session_mem_recv() but mem buffer is still not full. In
|
nghttp2_session_mem_recv() but mem buffer is still not full. In
|
||||||
|
@ -129,6 +131,7 @@ static void cf_h2_ctx_clear(struct cf_h2_ctx *ctx)
|
||||||
nghttp2_session_del(ctx->h2);
|
nghttp2_session_del(ctx->h2);
|
||||||
}
|
}
|
||||||
free(ctx->inbuf);
|
free(ctx->inbuf);
|
||||||
|
Curl_dyn_free(&ctx->outbuf);
|
||||||
memset(ctx, 0, sizeof(*ctx));
|
memset(ctx, 0, sizeof(*ctx));
|
||||||
ctx->call_data = save;
|
ctx->call_data = save;
|
||||||
}
|
}
|
||||||
|
@ -243,6 +246,8 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
|
||||||
ctx->inbuf = malloc(H2_BUFSIZE);
|
ctx->inbuf = malloc(H2_BUFSIZE);
|
||||||
if(!ctx->inbuf)
|
if(!ctx->inbuf)
|
||||||
goto out;
|
goto out;
|
||||||
|
/* we want to aggregate small frames, SETTINGS, PRIO, UPDATES */
|
||||||
|
Curl_dyn_init(&ctx->outbuf, 4*1024);
|
||||||
|
|
||||||
rc = nghttp2_session_callbacks_new(&cbs);
|
rc = nghttp2_session_callbacks_new(&cbs);
|
||||||
if(rc) {
|
if(rc) {
|
||||||
|
@ -337,7 +342,7 @@ out:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int h2_session_send(struct Curl_cfilter *cf,
|
static CURLcode h2_session_send(struct Curl_cfilter *cf,
|
||||||
struct Curl_easy *data);
|
struct Curl_easy *data);
|
||||||
static int h2_process_pending_input(struct Curl_cfilter *cf,
|
static int h2_process_pending_input(struct Curl_cfilter *cf,
|
||||||
struct Curl_easy *data,
|
struct Curl_easy *data,
|
||||||
|
@ -443,6 +448,32 @@ void Curl_http2_ver(char *p, size_t len)
|
||||||
(void)msnprintf(p, len, "nghttp2/%s", h2->version_str);
|
(void)msnprintf(p, len, "nghttp2/%s", h2->version_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CURLcode flush_output(struct Curl_cfilter *cf,
|
||||||
|
struct Curl_easy *data)
|
||||||
|
{
|
||||||
|
struct cf_h2_ctx *ctx = cf->ctx;
|
||||||
|
size_t buflen = Curl_dyn_len(&ctx->outbuf);
|
||||||
|
ssize_t written;
|
||||||
|
CURLcode result;
|
||||||
|
|
||||||
|
if(!buflen)
|
||||||
|
return CURLE_OK;
|
||||||
|
|
||||||
|
DEBUGF(LOG_CF(data, cf, "h2 conn flush %zu bytes", buflen));
|
||||||
|
written = Curl_conn_cf_send(cf->next, data, Curl_dyn_ptr(&ctx->outbuf),
|
||||||
|
buflen, &result);
|
||||||
|
if(written < 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if((size_t)written < buflen) {
|
||||||
|
Curl_dyn_tail(&ctx->outbuf, buflen - (size_t)written);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Curl_dyn_reset(&ctx->outbuf);
|
||||||
|
}
|
||||||
|
return CURLE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The implementation of nghttp2_send_callback type. Here we write |data| with
|
* The implementation of nghttp2_send_callback type. Here we write |data| with
|
||||||
* size |length| to the network and return the number of bytes actually
|
* size |length| to the network and return the number of bytes actually
|
||||||
|
@ -453,14 +484,37 @@ static ssize_t send_callback(nghttp2_session *h2,
|
||||||
void *userp)
|
void *userp)
|
||||||
{
|
{
|
||||||
struct Curl_cfilter *cf = userp;
|
struct Curl_cfilter *cf = userp;
|
||||||
|
struct cf_h2_ctx *ctx = cf->ctx;
|
||||||
struct Curl_easy *data = CF_DATA_CURRENT(cf);
|
struct Curl_easy *data = CF_DATA_CURRENT(cf);
|
||||||
ssize_t written;
|
ssize_t written;
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
|
size_t buflen = Curl_dyn_len(&ctx->outbuf);
|
||||||
|
|
||||||
(void)h2;
|
(void)h2;
|
||||||
(void)flags;
|
(void)flags;
|
||||||
DEBUGASSERT(data);
|
DEBUGASSERT(data);
|
||||||
|
|
||||||
|
if(blen < 1024 && (buflen + blen + 1 < ctx->outbuf.toobig)) {
|
||||||
|
result = Curl_dyn_addn(&ctx->outbuf, buf, blen);
|
||||||
|
if(result) {
|
||||||
|
failf(data, "Failed to add data to output buffer");
|
||||||
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
|
}
|
||||||
|
return blen;
|
||||||
|
}
|
||||||
|
if(buflen) {
|
||||||
|
/* not adding, flush buffer */
|
||||||
|
result = flush_output(cf, data);
|
||||||
|
if(result) {
|
||||||
|
if(result == CURLE_AGAIN) {
|
||||||
|
return NGHTTP2_ERR_WOULDBLOCK;
|
||||||
|
}
|
||||||
|
failf(data, "Failed sending HTTP2 data");
|
||||||
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUGF(LOG_CF(data, cf, "h2 conn send %zu bytes", blen));
|
||||||
written = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
|
written = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
|
||||||
if(result == CURLE_AGAIN) {
|
if(result == CURLE_AGAIN) {
|
||||||
return NGHTTP2_ERR_WOULDBLOCK;
|
return NGHTTP2_ERR_WOULDBLOCK;
|
||||||
|
@ -1401,11 +1455,11 @@ static int h2_process_pending_input(struct Curl_cfilter *cf,
|
||||||
{
|
{
|
||||||
struct cf_h2_ctx *ctx = cf->ctx;
|
struct cf_h2_ctx *ctx = cf->ctx;
|
||||||
ssize_t nread;
|
ssize_t nread;
|
||||||
char *inbuf;
|
|
||||||
ssize_t rv;
|
ssize_t rv;
|
||||||
|
|
||||||
nread = ctx->inbuflen - ctx->nread_inbuf;
|
nread = ctx->inbuflen - ctx->nread_inbuf;
|
||||||
inbuf = ctx->inbuf + ctx->nread_inbuf;
|
if(nread) {
|
||||||
|
char *inbuf = ctx->inbuf + ctx->nread_inbuf;
|
||||||
|
|
||||||
rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)inbuf, nread);
|
rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)inbuf, nread);
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
|
@ -1427,6 +1481,7 @@ static int h2_process_pending_input(struct Curl_cfilter *cf,
|
||||||
"in connection buffer",
|
"in connection buffer",
|
||||||
ctx->inbuflen - ctx->nread_inbuf));
|
ctx->inbuflen - ctx->nread_inbuf));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rv = h2_session_send(cf, data);
|
rv = h2_session_send(cf, data);
|
||||||
if(rv) {
|
if(rv) {
|
||||||
|
@ -1616,17 +1671,18 @@ static void h2_pri_spec(struct Curl_easy *data,
|
||||||
* dependency settings and if so it submits a PRIORITY frame with the updated
|
* dependency settings and if so it submits a PRIORITY frame with the updated
|
||||||
* info.
|
* info.
|
||||||
*/
|
*/
|
||||||
static int h2_session_send(struct Curl_cfilter *cf, struct Curl_easy *data)
|
static CURLcode h2_session_send(struct Curl_cfilter *cf,
|
||||||
|
struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
struct cf_h2_ctx *ctx = cf->ctx;
|
struct cf_h2_ctx *ctx = cf->ctx;
|
||||||
struct HTTP *stream = data->req.p.http;
|
struct HTTP *stream = data->req.p.http;
|
||||||
|
int rv = 0;
|
||||||
|
|
||||||
if((sweight_wanted(data) != sweight_in_effect(data)) ||
|
if((sweight_wanted(data) != sweight_in_effect(data)) ||
|
||||||
(data->set.priority.exclusive != data->state.priority.exclusive) ||
|
(data->set.priority.exclusive != data->state.priority.exclusive) ||
|
||||||
(data->set.priority.parent != data->state.priority.parent) ) {
|
(data->set.priority.parent != data->state.priority.parent) ) {
|
||||||
/* send new weight and/or dependency */
|
/* send new weight and/or dependency */
|
||||||
nghttp2_priority_spec pri_spec;
|
nghttp2_priority_spec pri_spec;
|
||||||
int rv;
|
|
||||||
|
|
||||||
h2_pri_spec(data, &pri_spec);
|
h2_pri_spec(data, &pri_spec);
|
||||||
DEBUGF(LOG_CF(data, cf, "[h2sid=%u] Queuing PRIORITY",
|
DEBUGF(LOG_CF(data, cf, "[h2sid=%u] Queuing PRIORITY",
|
||||||
|
@ -1635,10 +1691,17 @@ static int h2_session_send(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||||
rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE,
|
rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE,
|
||||||
stream->stream_id, &pri_spec);
|
stream->stream_id, &pri_spec);
|
||||||
if(rv)
|
if(rv)
|
||||||
return rv;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nghttp2_session_send(ctx->h2);
|
rv = nghttp2_session_send(ctx->h2);
|
||||||
|
out:
|
||||||
|
if(nghttp2_is_fatal(rv)) {
|
||||||
|
DEBUGF(LOG_CF(data, cf, "nghttp2_session_send error (%s)%d",
|
||||||
|
nghttp2_strerror(rv), rv));
|
||||||
|
return CURLE_SEND_ERROR;
|
||||||
|
}
|
||||||
|
return flush_output(cf, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||||
|
@ -1929,9 +1992,9 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||||
len = -1;
|
len = -1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
rv = h2_session_send(cf, data);
|
result = h2_session_send(cf, data);
|
||||||
if(nghttp2_is_fatal(rv)) {
|
if(result) {
|
||||||
*err = CURLE_SEND_ERROR;
|
*err = result;
|
||||||
len = -1;
|
len = -1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -2040,12 +2103,9 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||||
stream_id, (void *)data);
|
stream_id, (void *)data);
|
||||||
stream->stream_id = stream_id;
|
stream->stream_id = stream_id;
|
||||||
|
|
||||||
rv = h2_session_send(cf, data);
|
result = h2_session_send(cf, data);
|
||||||
if(rv) {
|
if(result) {
|
||||||
DEBUGF(LOG_CF(data, cf, "send: nghttp2_session_send error (%s)%d",
|
*err = result;
|
||||||
nghttp2_strerror(rv), rv));
|
|
||||||
|
|
||||||
*err = CURLE_SEND_ERROR;
|
|
||||||
len = -1;
|
len = -1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -2181,6 +2241,8 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf,
|
||||||
if(ctx && ctx->h2) {
|
if(ctx && ctx->h2) {
|
||||||
struct HTTP *stream = data->req.p.http;
|
struct HTTP *stream = data->req.p.http;
|
||||||
uint32_t window = !pause * HTTP2_HUGE_WINDOW_SIZE;
|
uint32_t window = !pause * HTTP2_HUGE_WINDOW_SIZE;
|
||||||
|
CURLcode result;
|
||||||
|
|
||||||
int rv = nghttp2_session_set_local_window_size(ctx->h2,
|
int rv = nghttp2_session_set_local_window_size(ctx->h2,
|
||||||
NGHTTP2_FLAG_NONE,
|
NGHTTP2_FLAG_NONE,
|
||||||
stream->stream_id,
|
stream->stream_id,
|
||||||
|
@ -2192,9 +2254,9 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* make sure the window update gets sent */
|
/* make sure the window update gets sent */
|
||||||
rv = h2_session_send(cf, data);
|
result = h2_session_send(cf, data);
|
||||||
if(rv)
|
if(result)
|
||||||
return CURLE_SEND_ERROR;
|
return result;
|
||||||
|
|
||||||
DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u",
|
DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u",
|
||||||
window, stream->stream_id));
|
window, stream->stream_id));
|
||||||
|
|
Loading…
Reference in New Issue
Block a user