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;
CURLcode result;
(void)Request;
- if(stream->recv_header_complete) {
+ if(!stream || stream->recv_header_complete) {
return;
}
* length (buflen is an inout parameter).
*/
(void)Request;
+ if(!stream)
+ return FALSE;
+
msh3_lock_acquire(&stream->recv_lock);
if(!stream->recv_header_complete) {
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;
{
struct Curl_easy *data = IfContext;
struct stream_ctx *stream = H3_STREAM_CTX(data);
+
+ if(!stream)
+ return;
(void)Request;
(void)stream;
}
{
struct Curl_easy *data = IfContext;
struct stream_ctx *stream = H3_STREAM_CTX(data);
+ if(!stream)
+ return;
(void)Request;
(void)stream;
(void)SendContext;
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");
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));
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));
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)));
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;
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) {
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)",
(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;
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));
(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) {
(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;
ssize_t nread = -1;
(void)cf;
-
+ DEBUGASSERT(stream);
if(stream->reset) {
failf(data,
"HTTP/3 stream %" PRId64 " reset by server", stream->id);
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);
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;
}
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. */
(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`
{
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,
}
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:
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;
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;
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) {
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)
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:
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);
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);
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 */
}
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,
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;
}
{
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)) {
{
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,
}
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;
-
- 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));
+ 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));
+ }
break;
}
case CF_CTRL_DATA_IDLE: