http2: store upload state per stream
authorDaniel Stenberg <daniel@haxx.se>
Mon, 18 May 2015 12:01:38 +0000 (14:01 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 18 May 2015 13:41:43 +0000 (15:41 +0200)
Use a curl_off_t for upload left

lib/http.h
lib/http2.c

index e8014409b2ab8298d2f37453d4822a1067f63f13..cb861e2e580c03bd89a57063a4d9c6266080a3ca 100644 (file)
@@ -171,6 +171,10 @@ struct HTTP {
   char *mem;     /* points to a buffer in memory to store received data */
   size_t len;    /* size of the buffer 'mem' points to */
   size_t memlen; /* size of data copied to mem */
+
+  const uint8_t *upload_mem; /* points to a buffer to read from */
+  size_t upload_len; /* size of the buffer 'upload_mem' points to */
+  curl_off_t upload_left; /* number of bytes left to upload */
 };
 
 typedef int (*sending)(void); /* Curl_send */
@@ -199,9 +203,6 @@ struct http_conn {
      nghttp2_session_mem_recv() but mem buffer is still not full. In
      this case, we wrongly sends the content of mem buffer if we share
      them for both cases. */
-  const uint8_t *upload_mem; /* points to a buffer to read from */
-  size_t upload_len; /* size of the buffer 'upload_mem' points to */
-  size_t upload_left; /* number of bytes left to upload */
   int32_t pause_stream_id; /* stream ID which paused
                               nghttp2_session_mem_recv */
 
index 867162a7669d77cbdd63e8604581c91e34ab47f0..c653bbe177de36a1e777ff20f50f1986cf69f289 100644 (file)
@@ -588,24 +588,47 @@ static ssize_t data_source_read_callback(nghttp2_session *session,
 {
   struct connectdata *conn = (struct connectdata *)userp;
   struct http_conn *c = &conn->proto.httpc;
+  struct SessionHandle *data_s;
+  struct HTTP *stream = NULL;
   size_t nread;
   (void)session;
   (void)stream_id;
   (void)source;
 
-  nread = MIN(c->upload_len, length);
+  if(stream_id) {
+    /* get the stream from the hash based on Stream ID, stream ID zero is for
+       connection-oriented stuff */
+    data_s = Curl_hash_pick(&c->streamsh, &stream_id, sizeof(stream_id));
+    if(!data_s) {
+      /* Receiving a Stream ID not in the hash should not happen, this is an
+         internal error more than anything else! */
+      failf(conn->data, "Asked for data to stream %x not in hash!", stream_id);
+      return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
+    stream = data_s->req.protop;
+  }
+  else {
+    failf(conn->data, "nghttp2 confusion");
+    return NGHTTP2_ERR_INVALID_ARGUMENT;
+  }
+
+  nread = MIN(stream->upload_len, length);
   if(nread > 0) {
-    memcpy(buf, c->upload_mem, nread);
-    c->upload_mem += nread;
-    c->upload_len -= nread;
-    c->upload_left -= nread;
+    memcpy(buf, stream->upload_mem, nread);
+    stream->upload_mem += nread;
+    stream->upload_len -= nread;
+    stream->upload_left -= nread;
   }
 
-  if(c->upload_left == 0)
+  if(stream->upload_left == 0)
     *data_flags = 1;
   else if(nread == 0)
     return NGHTTP2_ERR_DEFERRED;
 
+  DEBUGF(infof(data_s, "data_source_read_callback: "
+               "returns %zu bytes stream %x\n",
+               nread, stream_id));
+
   return nread;
 }
 
@@ -792,8 +815,8 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
 
   /* Nullify here because we call nghttp2_session_send() and they
      might refer to the old buffer. */
-  httpc->upload_mem = NULL;
-  httpc->upload_len = 0;
+  stream->upload_mem = NULL;
+  stream->upload_len = 0;
 
   /*
    * At this point 'stream' is just in the SessionHandle the connection
@@ -991,15 +1014,19 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
   if(stream->stream_id != -1) {
     /* If stream_id != -1, we have dispatched request HEADERS, and now
        are going to send or sending request body in DATA frame */
-    httpc->upload_mem = mem;
-    httpc->upload_len = len;
+    stream->upload_mem = mem;
+    stream->upload_len = len;
     nghttp2_session_resume_data(h2, stream->stream_id);
     rv = nghttp2_session_send(h2);
     if(nghttp2_is_fatal(rv)) {
       *err = CURLE_SEND_ERROR;
       return -1;
     }
-    return len - httpc->upload_len;
+    len -= stream->upload_len;
+
+    DEBUGF(infof(conn->data, "http2_send returns %zu for stream %x\n", len,
+                 stream->stream_id));
+    return len;
   }
 
   /* Calculate number of headers contained in [mem, mem + len) */
@@ -1078,12 +1105,15 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
     if(nva[i].namelen == 14 &&
        Curl_raw_nequal("content-length", (char*)nva[i].name, 14)) {
       size_t j;
+      stream->upload_left = 0;
       for(j = 0; j < nva[i].valuelen; ++j) {
-        httpc->upload_left *= 10;
-        httpc->upload_left += nva[i].value[j] - '0';
+        stream->upload_left *= 10;
+        stream->upload_left += nva[i].value[j] - '0';
       }
       DEBUGF(infof(conn->data,
-                   "request content-length=%zu\n", httpc->upload_left));
+                   "request content-length=%"
+                   CURL_FORMAT_CURL_OFF_T
+                   "\n", stream->upload_left));
     }
   }
 
@@ -1177,9 +1207,9 @@ CURLcode Curl_http2_setup(struct connectdata *conn)
     return result;
 
   infof(conn->data, "Using HTTP2, server supports multi-use\n");
-  httpc->upload_left = 0;
-  httpc->upload_mem = NULL;
-  httpc->upload_len = 0;
+  stream->upload_left = 0;
+  stream->upload_mem = NULL;
+  stream->upload_len = 0;
 
   httpc->inbuflen = 0;
   httpc->nread_inbuf = 0;