فهرست منبع

Add Chunked encoding support

Add support for chunked encoding to Civetweb, to enable streaming
http.
Keith Kyzivat 10 سال پیش
والد
کامیت
104db368a5
1فایلهای تغییر یافته به همراه70 افزوده شده و 2 حذف شده
  1. 70 2
      src/civetweb.c

+ 70 - 2
src/civetweb.c

@@ -883,6 +883,7 @@ struct mg_connection {
     int64_t num_bytes_sent;         /* Total bytes sent to client */
     int64_t content_len;            /* Content-Length header value */
     int64_t consumed_content;       /* How many bytes of content have been read */
+    int is_chunked;                 /* Transfer-encoding is chunked */
     char *buf;                      /* Buffer for received data */
     char *path_info;                /* PATH_INFO part of the URL */
     int must_close;                 /* 1 if connection must be closed */
@@ -2586,7 +2587,7 @@ static void discard_unread_request_data(struct mg_connection *conn)
     }
 }
 
-int mg_read(struct mg_connection *conn, void *buf, size_t len)
+int mg_read_inner(struct mg_connection *conn, void *buf, size_t len)
 {
     int64_t n, buffered_len, nread;
     int64_t len64 = (int64_t)(len > INT_MAX ? INT_MAX : len); /* since the return value is int, we may not read more bytes */
@@ -2632,6 +2633,69 @@ int mg_read(struct mg_connection *conn, void *buf, size_t len)
     return (int)nread;
 }
 
+static int mg_getc(struct mg_connection *conn)
+{
+  char c;
+  conn->content_len++;
+  if (mg_read_inner(conn, &c, 1) <= 0)
+  {
+      return EOF;
+  }
+  return (int)c;
+}
+
+int mg_read(struct mg_connection *conn, void *buf, size_t len)
+{
+    if (conn->is_chunked)
+    {
+        if (conn->content_len <= 0)
+        {
+            conn->content_len = 0;
+        }
+        if (conn->consumed_content < conn->content_len)
+        {
+            return mg_read_inner(conn, buf,len);
+        }
+
+        int i = 0;
+        char buf[64];
+        while (1)
+        {
+            int c = mg_getc(conn);
+            if (!(c == '\n' || c == '\r'))
+            {
+                buf[i++] = c;
+                break;
+            }
+        }
+        for (; i < (int)sizeof(buf); i++)
+        {
+            int c = mg_getc(conn);
+            if ( c == EOF )
+            {
+                return -1;
+            }
+            buf[i] = (char) c;
+            if (buf[i] == '\n' && buf[i-1] == '\r')
+            {
+                break;
+            }
+        }
+        char *end = 0;
+        long chunkSize = strtol(buf,&end,16);
+        if (end != buf+(i-1))
+        {
+            return -1;
+        }
+        if (chunkSize == 0)
+        {
+            return 0;
+        }
+        conn->content_len += chunkSize;
+    }
+    return mg_read_inner(conn,buf,len);
+}
+
 int mg_write(struct mg_connection *conn, const void *buf, size_t len)
 {
     time_t now;
@@ -4491,7 +4555,7 @@ static int forward_body_data(struct mg_connection *conn, FILE *fp,
     expect = mg_get_header(conn, "Expect");
     assert(fp != NULL);
 
-    if (conn->content_len == -1) {
+    if (conn->content_len == -1 && !conn->is_chunked) {
         /* Content length is not specified by the client. */
         send_http_error(conn, 411, "%s",
             "Error: Client did not specify content length");
@@ -7074,6 +7138,7 @@ static void reset_per_request_attributes(struct mg_connection *conn)
     conn->path_info = NULL;
     conn->num_bytes_sent = conn->consumed_content = 0;
     conn->status_code = -1;
+    conn->is_chunked = 0;
     conn->must_close = conn->request_len = conn->throttle = 0;
     conn->request_info.content_length = -1;
     conn->data_len = 0;
@@ -7313,10 +7378,13 @@ static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *
             }
             /* Publish the content length back to the request info. */
             conn->request_info.content_length = conn->content_len;
+        } else if (( cl = get_header(&conn->request_info, "Transfer-encoding")) != NULL && strcmp(cl,"chunked") == 0) {
+            conn->is_chunked = 1;
         } else if (!mg_strcasecmp(conn->request_info.request_method, "POST") ||
                    !mg_strcasecmp(conn->request_info.request_method, "PUT")) {
             /* POST or PUT request without content length set */
             conn->content_len = -1;
+            conn->content_len = 0;
         } else if (!mg_strncasecmp(conn->request_info.request_method, "HTTP/", 5)) {
             /* Response without content length set */
             conn->content_len = -1;