Using PUT without a content-length

Joe Orton joe at manyfish.co.uk
Sun Aug 3 15:59:07 EDT 2008


After looking at this more, it's simpler to actually avoid writev() and 
just reserve space in the buffer to write the chunk-size etc over after 
invoking the callback - can you try this out?

Index: src/ne_request.c
===================================================================
--- src/ne_request.c	(revision 1521)
+++ src/ne_request.c	(working copy)
@@ -336,6 +336,13 @@
 ((((code) == NE_SOCK_CLOSED || (code) == NE_SOCK_RESET || \
  (code) == NE_SOCK_TRUNC) && retry) ? NE_RETRY : (acode))
 
+/* For sending chunks, an 8-byte prefix is reserved at the beginning
+ * of the buffer.  This is large enough for a trailing \r\n for the
+ * previous chunk, the chunk size, and the \r\n following the
+ * chunk-size. */
+#define CHUNK_OFFSET (8)
+#define CHUNK_TERM "\r\n0\r\n\r\n"
+
 /* Sends the request body; returns 0 on success or an NE_* error code.
  * If retry is non-zero; will return NE_RETRY on persistent connection
  * timeout.  On error, the session error string is set and the
@@ -343,13 +350,27 @@
 static int send_request_body(ne_request *req, int retry)
 {
     ne_session *const sess = req->session;
-    char buffer[NE_BUFSIZ];
+    char buffer[NE_BUFSIZ], *start;
     ssize_t bytes;
+    size_t buflen;
+    int chunked = req->body_length < 0, chunknum = 0;
+    int ret;
 
     NE_DEBUG(NE_DBG_HTTP, "Sending request body:\n");
 
+    /* Set up status union and (start, buflen) as the buffer to be
+     * passed the supplied callback. */
+    if (chunked) {
+        start = buffer + CHUNK_OFFSET;
+        buflen = sizeof(buffer) - CHUNK_OFFSET;
+        req->session->status.sr.total = -1;
+    }
+    else {
+        start = buffer;
+        buflen = sizeof buffer;
+        req->session->status.sr.total = req->body_length;
+    }
     req->session->status.sr.progress = 0;
-    req->session->status.sr.total = req->body_length;
     notify_status(sess, ne_status_sending);
     
     /* tell the source to start again from the beginning. */
@@ -358,8 +379,24 @@
         return NE_ERROR;
     }
     
-    while ((bytes = req->body_cb(req->body_ud, buffer, sizeof buffer)) > 0) {
-	int ret = ne_sock_fullwrite(sess->socket, buffer, bytes);
+
+    while ((bytes = req->body_cb(req->body_ud, start, buflen)) > 0) {
+        req->session->status.sr.progress += bytes;
+
+        if (chunked) {
+            /* Overwrite the buffer prefix with the appropriate chunk
+             * size; since ne_snprintf always NUL-terminates, the \n
+             * is omitted and placed over the NUL afterwards. */
+            if (chunknum++ == 0)
+                ne_snprintf(buffer, CHUNK_OFFSET, 
+                            "%06" NE_FMT_SSIZE_T "\r", bytes);
+            else
+                ne_snprintf(buffer, CHUNK_OFFSET, 
+                            "\r\n%04" NE_FMT_SSIZE_T "\r", bytes);
+            buffer[CHUNK_OFFSET - 1] = '\n';
+            bytes += CHUNK_OFFSET;
+        }
+        ret = ne_sock_fullwrite(sess->socket, buffer, bytes);
         if (ret < 0) {
             int aret = aborted(req, _("Could not send request body"), ret);
             return RETRY_RET(retry, ret, aret);
@@ -370,18 +407,27 @@
 		 bytes, (int)bytes, buffer);
 
         /* invoke progress callback */
-        req->session->status.sr.progress += bytes;
         notify_status(sess, ne_status_sending);
     }
 
-    if (bytes == 0) {
-        return NE_OK;
-    } else {
+    if (bytes) {
         NE_DEBUG(NE_DBG_HTTP, "Request body provider failed with "
                  "%" NE_FMT_SSIZE_T "\n", bytes);
         ne_close_connection(sess);
         return NE_ERROR;
     }
+
+    if (chunked) {
+        ret = ne_sock_fullwrite(sess->socket, CHUNK_TERM, 
+                                sizeof(CHUNK_TERM) - 1);
+        if (ret < 0) {
+            int aret = aborted(req, _("Could not send chunked "
+                                      "request terminator"), ret);
+            return RETRY_RET(retry, ret, aret);
+        }
+    }
+    
+    return NE_OK;
 }
 
 /* Lob the User-Agent, connection and host headers in to the request
@@ -470,7 +516,10 @@
 static void set_body_length(ne_request *req, ne_off_t length)
 {
     req->body_length = length;
-    ne_print_request_header(req, "Content-Length", "%" FMT_NE_OFF_T, length);
+    if (length > 0)
+        ne_print_request_header(req, "Content-Length", "%" FMT_NE_OFF_T, length);
+    else if (length < 0)
+        ne_print_request_header(req, "Transfer-Encoding", "chunked");
 }
 
 void ne_set_request_body_buffer(ne_request *req, const char *buffer,
@@ -941,7 +990,7 @@
 	return RETRY_RET(retry, sret, aret);
     }
     
-    if (!req->flags[NE_REQFLAG_EXPECT100] && req->body_length > 0) {
+    if (!req->flags[NE_REQFLAG_EXPECT100] && req->body_length != 0) {
 	/* Send request body, if not using 100-continue. */
 	ret = send_request_body(req, retry);
 	if (ret) {
@@ -961,7 +1010,7 @@
 	if ((ret = discard_headers(req)) != NE_OK) break;
 
 	if (req->flags[NE_REQFLAG_EXPECT100] && (status->code == 100)
-            && req->body_length > 0 && !sentbody) {
+            && req->body_length != 0 && !sentbody) {
 	    /* Send the body after receiving the first 100 Continue */
 	    if ((ret = send_request_body(req, 0)) != NE_OK) break;	    
 	    sentbody = 1;





More information about the neon mailing list