]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
ngtcp2: fix HTTP/3 upload stall and avoid busy loop
authorTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Sun, 10 Apr 2022 21:07:29 +0000 (23:07 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Sun, 10 Apr 2022 21:07:42 +0000 (23:07 +0200)
This commit fixes HTTP/3 upload stall if upload data is larger than
H3_SEND_SIZE.  Only check writability of socket if a stream is
writable to avoid busy loop when QUIC flow control window is filled
up, or upload buffer is full.

Closes #8688

lib/vquic/ngtcp2.c

index 1d73e27d0b00d1a4d066846e77dcd5bfd2a099a5..9dd4fa21476a03b82008e0818d16f937a7d7f205 100644 (file)
@@ -867,6 +867,8 @@ static int ng_getsock(struct Curl_easy *data, struct connectdata *conn,
 {
   struct SingleRequest *k = &data->req;
   int bitmap = GETSOCK_BLANK;
+  struct HTTP *stream = data->req.p.http;
+  struct quicsocket *qs = conn->quic;
 
   socks[0] = conn->sock[FIRSTSOCKET];
 
@@ -875,7 +877,10 @@ static int ng_getsock(struct Curl_easy *data, struct connectdata *conn,
   bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
 
   /* we're still uploading or the HTTP/2 layer wants to send data */
-  if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND)
+  if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND &&
+     (!stream->h3out || stream->h3out->used < H3_SEND_SIZE) &&
+     ngtcp2_conn_get_max_data_left(qs->qconn) &&
+     nghttp3_conn_is_stream_writable(qs->h3conn, stream->stream3_id))
     bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
 
   return bitmap;
@@ -1375,6 +1380,10 @@ static ssize_t cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id,
     return 1;
   }
 
+  if(stream->upload_len && H3_SEND_SIZE <= stream->h3out->used) {
+    return NGHTTP3_ERR_WOULDBLOCK;
+  }
+
   nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used);
   if(nread > 0) {
     /* nghttp3 wants us to hold on to the data until it tells us it is okay to
@@ -1527,7 +1536,7 @@ static ssize_t ngh3_stream_send(struct Curl_easy *data,
                                 size_t len,
                                 CURLcode *curlcode)
 {
-  ssize_t sent;
+  ssize_t sent = 0;
   struct connectdata *conn = data->conn;
   struct quicsocket *qs = conn->quic;
   curl_socket_t sockfd = conn->sock[sockindex];
@@ -1539,6 +1548,9 @@ static ssize_t ngh3_stream_send(struct Curl_easy *data,
       *curlcode = CURLE_SEND_ERROR;
       return -1;
     }
+    /* Assume that mem of length len only includes HTTP/1.1 style
+       header fields.  In other words, it does not contain request
+       body. */
     sent = len;
   }
   else {
@@ -1548,7 +1560,6 @@ static ssize_t ngh3_stream_send(struct Curl_easy *data,
       stream->upload_mem = mem;
       stream->upload_len = len;
       (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id);
-      sent = len;
     }
     else {
       *curlcode = CURLE_AGAIN;
@@ -1563,8 +1574,20 @@ static ssize_t ngh3_stream_send(struct Curl_easy *data,
 
   /* Reset post upload buffer after resumed. */
   if(stream->upload_mem) {
+    if(data->set.postfields) {
+      sent = len;
+    }
+    else {
+      sent = len - stream->upload_len;
+    }
+
     stream->upload_mem = NULL;
     stream->upload_len = 0;
+
+    if(sent == 0) {
+      *curlcode = CURLE_AGAIN;
+      return -1;
+    }
   }
 
   *curlcode = CURLE_OK;