mirror of
https://github.com/curl/curl.git
synced 2026-07-24 08:47:50 +03:00
http3: check stream_ctx more thoroughly in all backends
- callbacks and filter methods might be invoked at unexpected times, e.g. when the transfer's stream_ctx has not been initialized yet or, more likely, has already been taken down. - check for existance of stream_ctx in such places and return an error or silently succeed the call. Closes #10951
This commit is contained in:
parent
7e68133d04
commit
be800a6cab
3 changed files with 108 additions and 25 deletions
|
|
@ -288,6 +288,9 @@ static CURLcode write_resp_raw(struct Curl_easy *data,
|
|||
CURLcode result = CURLE_OK;
|
||||
ssize_t nwritten;
|
||||
|
||||
if(!stream)
|
||||
return CURLE_RECV_ERROR;
|
||||
|
||||
nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
|
||||
if(nwritten < 0) {
|
||||
return result;
|
||||
|
|
@ -311,7 +314,7 @@ static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
|
|||
CURLcode result;
|
||||
(void)Request;
|
||||
|
||||
if(stream->recv_header_complete) {
|
||||
if(!stream || stream->recv_header_complete) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -366,6 +369,9 @@ static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
|
|||
* length (buflen is an inout parameter).
|
||||
*/
|
||||
(void)Request;
|
||||
if(!stream)
|
||||
return FALSE;
|
||||
|
||||
msh3_lock_acquire(&stream->recv_lock);
|
||||
|
||||
if(!stream->recv_header_complete) {
|
||||
|
|
@ -395,6 +401,8 @@ static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
|
|||
struct stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
|
||||
(void)Request;
|
||||
if(!stream)
|
||||
return;
|
||||
msh3_lock_acquire(&stream->recv_lock);
|
||||
stream->closed = TRUE;
|
||||
stream->recv_header_complete = true;
|
||||
|
|
@ -410,6 +418,9 @@ static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
|
|||
{
|
||||
struct Curl_easy *data = IfContext;
|
||||
struct stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
|
||||
if(!stream)
|
||||
return;
|
||||
(void)Request;
|
||||
(void)stream;
|
||||
}
|
||||
|
|
@ -419,6 +430,8 @@ static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
|
|||
{
|
||||
struct Curl_easy *data = IfContext;
|
||||
struct stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
if(!stream)
|
||||
return;
|
||||
(void)Request;
|
||||
(void)stream;
|
||||
(void)SendContext;
|
||||
|
|
@ -431,6 +444,10 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
|
|||
struct stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
ssize_t nread = -1;
|
||||
|
||||
if(!stream) {
|
||||
*err = CURLE_RECV_ERROR;
|
||||
return -1;
|
||||
}
|
||||
(void)cf;
|
||||
if(stream->reset) {
|
||||
failf(data, "HTTP/3 stream reset by server");
|
||||
|
|
@ -480,6 +497,10 @@ static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|||
struct cf_call_data save;
|
||||
|
||||
(void)cf;
|
||||
if(!stream) {
|
||||
*err = CURLE_RECV_ERROR;
|
||||
return -1;
|
||||
}
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
DEBUGF(LOG_CF(data, cf, "req: recv with %zu byte buffer", len));
|
||||
|
||||
|
|
@ -535,6 +556,7 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|||
CF_DATA_SAVE(save, cf, data);
|
||||
|
||||
/* Sizes must match for cast below to work" */
|
||||
DEBUGASSERT(stream);
|
||||
DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo));
|
||||
DEBUGF(LOG_CF(data, cf, "req: send %zu bytes", len));
|
||||
|
||||
|
|
@ -628,7 +650,7 @@ static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
|
|||
CF_DATA_SAVE(save, cf, data);
|
||||
|
||||
(void)cf;
|
||||
if(stream->req) {
|
||||
if(stream && stream->req) {
|
||||
msh3_lock_acquire(&stream->recv_lock);
|
||||
DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data pending = %zu",
|
||||
Curl_bufq_len(&stream->recvbuf)));
|
||||
|
|
@ -678,11 +700,14 @@ static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf,
|
|||
break;
|
||||
case CF_CTRL_DATA_DONE_SEND:
|
||||
DEBUGF(LOG_CF(data, cf, "req: send done"));
|
||||
stream->upload_done = TRUE;
|
||||
if(stream && stream->req) {
|
||||
char buf[1];
|
||||
if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN, buf, 0, data)) {
|
||||
result = CURLE_SEND_ERROR;
|
||||
if(stream) {
|
||||
stream->upload_done = TRUE;
|
||||
if(stream->req) {
|
||||
char buf[1];
|
||||
if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN,
|
||||
buf, 0, data)) {
|
||||
result = CURLE_SEND_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -688,6 +688,8 @@ static void report_consumed_data(struct Curl_cfilter *cf,
|
|||
struct stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
struct cf_ngtcp2_ctx *ctx = cf->ctx;
|
||||
|
||||
if(!stream)
|
||||
return;
|
||||
/* the HTTP/1.1 response headers are written to the buffer, but
|
||||
* consuming those does not count against flow control. */
|
||||
if(stream->recv_buf_nonflow) {
|
||||
|
|
@ -984,7 +986,7 @@ static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf,
|
|||
if((k->keepon & KEEP_SENDBITS) == KEEP_SEND &&
|
||||
ngtcp2_conn_get_cwnd_left(ctx->qconn) &&
|
||||
ngtcp2_conn_get_max_data_left(ctx->qconn) &&
|
||||
nghttp3_conn_is_stream_writable(ctx->h3conn, stream->id))
|
||||
stream && nghttp3_conn_is_stream_writable(ctx->h3conn, stream->id))
|
||||
rv |= GETSOCK_WRITESOCK(0);
|
||||
|
||||
DEBUGF(LOG_CF(data, cf, "get_select_socks -> %x (sock=%d)",
|
||||
|
|
@ -1015,6 +1017,10 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
|
|||
(void)app_error_code;
|
||||
(void)cf;
|
||||
|
||||
/* we might be called by nghttp3 after we already cleaned up */
|
||||
if(!stream)
|
||||
return 0;
|
||||
|
||||
DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] h3 close(err=%" PRId64 ")",
|
||||
stream_id, app_error_code));
|
||||
stream->closed = TRUE;
|
||||
|
|
@ -1041,6 +1047,9 @@ static CURLcode write_resp_raw(struct Curl_cfilter *cf,
|
|||
ssize_t nwritten;
|
||||
|
||||
(void)cf;
|
||||
if(!stream) {
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
|
||||
/* DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] add recvbuf(len=%zu) "
|
||||
"-> %zd, %d", stream->id, memlen, nwritten, result));
|
||||
|
|
@ -1107,6 +1116,8 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
|
|||
(void)fin;
|
||||
(void)cf;
|
||||
|
||||
if(!stream)
|
||||
return 0;
|
||||
/* add a CRLF only if we've received some headers */
|
||||
result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
|
||||
if(result) {
|
||||
|
|
@ -1141,6 +1152,10 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
|
|||
(void)flags;
|
||||
(void)cf;
|
||||
|
||||
/* we might have cleaned up this transfer already */
|
||||
if(!stream)
|
||||
return 0;
|
||||
|
||||
if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
|
||||
char line[14]; /* status line is always 13 characters long */
|
||||
size_t ncopy;
|
||||
|
|
@ -1306,7 +1321,7 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
|
|||
ssize_t nread = -1;
|
||||
|
||||
(void)cf;
|
||||
|
||||
DEBUGASSERT(stream);
|
||||
if(stream->reset) {
|
||||
failf(data,
|
||||
"HTTP/3 stream %" PRId64 " reset by server", stream->id);
|
||||
|
|
@ -1365,6 +1380,11 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|||
DEBUGASSERT(ctx->h3conn);
|
||||
*err = CURLE_OK;
|
||||
|
||||
if(!stream) {
|
||||
*err = CURLE_RECV_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(!Curl_bufq_is_empty(&stream->recvbuf)) {
|
||||
nread = Curl_bufq_read(&stream->recvbuf,
|
||||
(unsigned char *)buf, len, err);
|
||||
|
|
@ -1414,7 +1434,7 @@ out:
|
|||
goto out;
|
||||
}
|
||||
DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv(len=%zu) -> %zd, %d",
|
||||
stream->id, len, nread, *err));
|
||||
stream? stream->id : -1, len, nread, *err));
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
return nread;
|
||||
}
|
||||
|
|
@ -1428,6 +1448,8 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id,
|
|||
struct stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
|
||||
(void)cf;
|
||||
if(!stream)
|
||||
return 0;
|
||||
/* The server ackknowledged `datalen` of bytes from our request body.
|
||||
* This is a delta. We have kept this data in `sendbuf` for
|
||||
* re-transmissions and can free it now. */
|
||||
|
|
@ -1471,6 +1493,8 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
|
|||
(void)user_data;
|
||||
(void)veccnt;
|
||||
|
||||
if(!stream)
|
||||
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
||||
/* nghttp3 keeps references to the sendbuf data until it is ACKed
|
||||
* by the server (see `cb_h3_acked_req_body()` for updates).
|
||||
* `sendbuf_len_in_flight` is the amount of bytes in `sendbuf`
|
||||
|
|
@ -2065,7 +2089,7 @@ static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf,
|
|||
{
|
||||
const struct stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
(void)cf;
|
||||
return !Curl_bufq_is_empty(&stream->recvbuf);
|
||||
return stream && !Curl_bufq_is_empty(&stream->recvbuf);
|
||||
}
|
||||
|
||||
static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf,
|
||||
|
|
@ -2088,8 +2112,10 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf,
|
|||
}
|
||||
case CF_CTRL_DATA_DONE_SEND: {
|
||||
struct stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
stream->upload_done = TRUE;
|
||||
(void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id);
|
||||
if(stream) {
|
||||
stream->upload_done = TRUE;
|
||||
(void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CF_CTRL_DATA_IDLE:
|
||||
|
|
|
|||
|
|
@ -344,6 +344,8 @@ static CURLcode write_resp_raw(struct Curl_cfilter *cf,
|
|||
ssize_t nwritten;
|
||||
|
||||
(void)cf;
|
||||
if(!stream)
|
||||
return CURLE_RECV_ERROR;
|
||||
nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
|
||||
if(nwritten < 0)
|
||||
return result;
|
||||
|
|
@ -390,7 +392,7 @@ static int cb_each_header(uint8_t *name, size_t name_len,
|
|||
if(result) {
|
||||
DEBUGF(LOG_CF(x->data, x->cf,
|
||||
"[h3sid=%"PRId64"][HEADERS][%.*s: %.*s] error %d",
|
||||
stream->id, (int)name_len, name,
|
||||
stream? stream->id : -1, (int)name_len, name,
|
||||
(int)value_len, value, result));
|
||||
}
|
||||
return result;
|
||||
|
|
@ -405,6 +407,11 @@ static ssize_t stream_resp_read(void *reader_ctx,
|
|||
struct stream_ctx *stream = H3_STREAM_CTX(x->data);
|
||||
ssize_t nread;
|
||||
|
||||
if(!stream) {
|
||||
*err = CURLE_RECV_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
nread = quiche_h3_recv_body(ctx->h3c, ctx->qconn, stream->id,
|
||||
buf, len);
|
||||
if(nread >= 0) {
|
||||
|
|
@ -429,6 +436,9 @@ static CURLcode cf_recv_body(struct Curl_cfilter *cf,
|
|||
struct cb_ctx cb_ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(!stream)
|
||||
return CURLE_RECV_ERROR;
|
||||
|
||||
if(!stream->resp_hds_complete) {
|
||||
result = write_resp_raw(cf, data, "\r\n", 2);
|
||||
if(result)
|
||||
|
|
@ -486,6 +496,8 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf,
|
|||
CURLcode result = CURLE_OK;
|
||||
int rc;
|
||||
|
||||
if(!stream)
|
||||
return CURLE_OK;
|
||||
DEBUGASSERT(stream3_id == stream->id);
|
||||
switch(quiche_h3_event_type(ev)) {
|
||||
case QUICHE_H3_EVENT_HEADERS:
|
||||
|
|
@ -764,6 +776,7 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
|
|||
struct stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
ssize_t nread = -1;
|
||||
|
||||
DEBUGASSERT(stream);
|
||||
if(stream->reset) {
|
||||
failf(data,
|
||||
"HTTP/3 stream %" PRId64 " reset by server", stream->id);
|
||||
|
|
@ -798,6 +811,11 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|||
ssize_t nread = -1;
|
||||
CURLcode result;
|
||||
|
||||
if(!stream) {
|
||||
*err = CURLE_RECV_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(!Curl_bufq_is_empty(&stream->recvbuf)) {
|
||||
nread = Curl_bufq_read(&stream->recvbuf,
|
||||
(unsigned char *)buf, len, err);
|
||||
|
|
@ -868,6 +886,14 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf,
|
|||
quiche_h3_header *nva = NULL;
|
||||
struct h2h3req *hreq = NULL;
|
||||
|
||||
if(!stream) {
|
||||
*err = h3_data_setup(cf, data);
|
||||
if(*err)
|
||||
goto fail;
|
||||
stream = H3_STREAM_CTX(data);
|
||||
DEBUGASSERT(stream);
|
||||
}
|
||||
|
||||
if(!stream->req_hds_len) {
|
||||
stream->req_hds_len = len; /* fist call */
|
||||
}
|
||||
|
|
@ -969,10 +995,11 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if(stream->id < 0) {
|
||||
if(!stream || stream->id < 0) {
|
||||
nwritten = h3_open_stream(cf, data, buf, len, err);
|
||||
if(nwritten < 0)
|
||||
goto out;
|
||||
stream = H3_STREAM_CTX(data);
|
||||
}
|
||||
else {
|
||||
nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id,
|
||||
|
|
@ -1019,7 +1046,7 @@ out:
|
|||
nwritten = -1;
|
||||
}
|
||||
DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu) -> %zd, %d",
|
||||
stream->id, len, nwritten, *err));
|
||||
stream? stream->id : -1, len, nwritten, *err));
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
|
|
@ -1028,10 +1055,13 @@ static bool stream_is_writeable(struct Curl_cfilter *cf,
|
|||
{
|
||||
struct cf_quiche_ctx *ctx = cf->ctx;
|
||||
struct stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
quiche_stream_iter *qiter;
|
||||
bool is_writable = FALSE;
|
||||
|
||||
if(!stream)
|
||||
return FALSE;
|
||||
/* surely, there must be a better way */
|
||||
quiche_stream_iter *qiter = quiche_conn_writable(ctx->qconn);
|
||||
qiter = quiche_conn_writable(ctx->qconn);
|
||||
if(qiter) {
|
||||
uint64_t stream_id;
|
||||
while(quiche_stream_iter_next(qiter, &stream_id)) {
|
||||
|
|
@ -1076,7 +1106,7 @@ static bool cf_quiche_data_pending(struct Curl_cfilter *cf,
|
|||
{
|
||||
const struct stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
(void)cf;
|
||||
return !Curl_bufq_is_empty(&stream->recvbuf);
|
||||
return stream && !Curl_bufq_is_empty(&stream->recvbuf);
|
||||
}
|
||||
|
||||
static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
|
||||
|
|
@ -1098,14 +1128,16 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
|
|||
}
|
||||
case CF_CTRL_DATA_DONE_SEND: {
|
||||
struct stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
unsigned char body[1];
|
||||
ssize_t sent;
|
||||
stream->upload_done = TRUE;
|
||||
if(stream) {
|
||||
unsigned char body[1];
|
||||
ssize_t sent;
|
||||
stream->upload_done = TRUE;
|
||||
|
||||
body[0] = 'X';
|
||||
sent = cf_quiche_send(cf, data, body, 0, &result);
|
||||
DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] DONE_SEND -> %zd, %d",
|
||||
stream->id, sent, result));
|
||||
body[0] = 'X';
|
||||
sent = cf_quiche_send(cf, data, body, 0, &result);
|
||||
DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] DONE_SEND -> %zd, %d",
|
||||
stream->id, sent, result));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CF_CTRL_DATA_IDLE:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue