Using PUT without a content-length

Julien Reichel Julien.Reichel at spinetix.com
Mon Jul 28 08:35:36 EDT 2008


The attached path is at the end of this mail.

Julien 
 
> Hi,
> > 1) ne_request.h needs updating for the new interface of 
> passing length 
> > == -1 to ne_set_request_body_provider().
> Done.
> > 
> > 2) sending chunks should really be done using writev(), so a new 
> > method would need to be exposed via ne_socket.* for that.
> Done. Not so sure it the nicest as possible (I don't like the 
> two mallocs, but did not come up with a better idea).
> I did not implemented the writev for SSL connection, so there 
> is a fallback using the send function if the writev is not 
> implemented.
> 
> > 
> > 3) in the status handling, sr.total should be set to -1 to indicate 
> > "unknown length", otherwise correct
> Done
> > 
> > 4) code style nits, no // comments, follow neon style 
> generally with 
> > e.g. "if (foo)" not "if ( foo )" etc ;)
> Hopefully done :-)
> 
> > Would you be willing to work on an updated patch for this 
> addressing 
> > some of these issues?
> Hopefully done with the attached patch.
> 
> Julien
> 
> Visit http://www.spinetix.com/ for more information 
> 



Index: ne_request.c
===================================================================
--- ne_request.c	(revision base)
+++ ne_request.c	(revision new)
@@ -340,12 +340,20 @@
 {
     ne_session *const sess = req->session;
     char buffer[NE_BUFSIZ];
+    char chunk_buffer[128];
+    int chunk_idx=0;
+    int ret = 0;
+    struct ne_iovec_s vector[2];
+    int count;
     ssize_t bytes;
 
     NE_DEBUG(NE_DBG_HTTP, "Sending request body:\n");
 
     req->session->status.sr.progress = 0;
-    req->session->status.sr.total = req->body_length;
+    if (req->body_length > 0)
+        req->session->status.sr.total = req->body_length;
+    else
+        req->session->status.sr.total = -1;
     notify_status(sess, ne_status_sending);
     
     /* tell the source to start again from the beginning. */
@@ -355,7 +363,22 @@
     }
     
     while ((bytes = req->body_cb(req->body_ud, buffer, sizeof buffer))
> 0) {
-	int ret = ne_sock_fullwrite(sess->socket, buffer, bytes);
+        count = 0;
+        if (req->body_length < 0) {
+            /* Chunked mode */
+            if (chunk_idx == 0)
+                snprintf(chunk_buffer, 128, "%x\r\n", (unsigned)bytes);
+            else
+                snprintf(chunk_buffer, 128, "\r\n%x\r\n",
(unsigned)bytes);
+            vector[count].base = chunk_buffer;
+            vector[count].len = strlen(chunk_buffer);
+            count++;
+            chunk_idx++;
+        }
+        vector[count].base = buffer;
+        vector[count].len = bytes;
+        count++;
+        ret = ne_sock_fullwritev(sess->socket, vector, count);
         if (ret < 0) {
             int aret = aborted(req, _("Could not send request body"),
ret);
             return RETRY_RET(retry, ret, aret);
@@ -369,6 +392,15 @@
         req->session->status.sr.progress += bytes;
         notify_status(sess, ne_status_sending);
     }
+    if (req->body_length < 0) {
+        /* Chunked mode */
+        snprintf(chunk_buffer, 128, "\r\n0\r\n\r\n");
+        ret = ne_sock_fullwrite(sess->socket, chunk_buffer,
strlen(chunk_buffer));
+        if (ret < 0) {
+            int aret = aborted(req, _("Could not send last-chunk
info"), ret);
+            return RETRY_RET(retry, ret, aret);
+        }
+    }
 
     if (bytes == 0) {
         return NE_OK;
@@ -466,7 +498,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,
@@ -937,7 +972,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) {
@@ -957,7 +992,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;
Index: ne_socket.c
===================================================================
--- ne_socket.c	(revision base)
+++ ne_socket.c	(revision new)
@@ -190,6 +190,9 @@
     /* Wait up to 'n' seconds for socket to become readable.  Returns
      * 0 when readable, otherwise NE_SOCK_TIMEOUT or NE_SOCK_ERROR. */
     int (*readable)(ne_socket *s, int n);
+    /* writes at most 'count' blocks described by 'vector' to socket.
Return number of
+     * bytes written on success, or <0 on error. */
+    ssize_t (*swritev)(ne_socket *s, const struct ne_iovec_s *vector,
int count);
 };
 
 static const ne_inet_addr dummy_laddr;
@@ -542,8 +545,44 @@
     return ret;
 }
 
-static const struct iofns iofns_raw = { read_raw, write_raw,
readable_raw };
+static ssize_t writev_raw(ne_socket *sock, const struct ne_iovec_s
*vector, int count) 
+{
+    ssize_t ret;
+#ifdef WIN32
+    LPWSABUF wasvector = (LPWSABUF)ne_malloc(count*sizeof(WSABUF));
+    int i;
+    DWORD numberbytes;
+    for (i=0; i<count; i++){
+        wasvector[i].buf=vector[i].base;
+        wasvector[i].len=vector[i].len;
+    }
+        
+    ret = WSASend(sock->fd, wasvector, count, &numberbytes, 0, NULL,
NULL);
+    
+    if (ret == 0)
+        ret = numberbytes;
+    
+    ne_free(wasvector);
+#else
+    const struct iovec* iovector=(const struct iovec*)vector;
+    do {
+	ret = writev(sock->fd, iovector, count);
+    } while (ret == -1 && NE_ISINTR(ne_errno));
 
+    
+#endif
+
+    if (ret < 0) {
+	int errnum = ne_errno;
+	set_strerror(sock, errnum);
+	return MAP_ERR(errnum);
+    }
+    
+    return ret;
+}
+
+static const struct iofns iofns_raw = { read_raw, write_raw,
readable_raw, writev_raw };
+
 #ifdef HAVE_OPENSSL
 /* OpenSSL I/O function implementations. */
 static int readable_ossl(ne_socket *sock, int secs)
@@ -626,7 +665,8 @@
 static const struct iofns iofns_ssl = {
     read_ossl,
     write_ossl,
-    readable_ossl
+    readable_ossl,
+    NULL
 };
 
 #elif defined(HAVE_GNUTLS)
@@ -731,7 +771,8 @@
 static const struct iofns iofns_ssl = {
     read_gnutls,
     write_gnutls,
-    readable_gnutls
+    readable_gnutls,
+    NULL
 };
 
 #endif
@@ -751,6 +792,56 @@
     return ret < 0 ? ret : 0;
 }
 
+int ne_sock_fullwritev(ne_socket *sock, const struct ne_iovec_s
*vector, int count)
+{
+    ssize_t ret;
+
+    if (!sock->ops->swritev || count==1) {
+        /* writev not supported or single element in the vector*/
+        int i;
+        for (i=0; i<count; i++) {
+            ret = ne_sock_fullwrite(sock, vector[i].base,
vector[i].len);
+            if (ret<0)
+                return ret;
+        }
+    }else{
+        size_t len;
+        int pos = 0;
+        int i, offset = 0;
+        const struct ne_iovec_s *temp = vector;
+        struct ne_iovec_s *alloctemp = NULL;
+        
+        do {
+            ret = sock->ops->swritev(sock, temp+offset, count-offset);
+            if (ret > 0) {
+                for (i=offset; i<count; i++) {
+                    if (ret >= temp[i].len)
+                        ret -= temp[i].len;
+                    else
+                        break;
+                }
+                if (i < count) {
+                    /* not all data was sent */
+                    if (!alloctemp) {
+                        alloctemp = ne_malloc(count*sizeof(struct
ne_iovec_s));
+                        temp = alloctemp;
+                        for (i=0; i<count; i++)
+                            alloctemp[i]=vector[i];
+                    }
+                    offset = i;
+                    alloctemp[offset].len -= ret;
+                    (unsigned char*)alloctemp[offset].base += ret;
+                }
+            }
+        } while (ret > 0 && i < count);
+
+        if (alloctemp)
+            ne_free(alloctemp);
+    }
+
+    return ret < 0 ? ret : 0;
+}
+
 ssize_t ne_sock_readline(ne_socket *sock, char *buf, size_t buflen)
 {
     char *lf;
Index: ne_request.h
===================================================================
--- ne_request.h	(revision base)
+++ ne_request.h	(revision new)
@@ -78,7 +78,9 @@
 /* Install a callback which is invoked as needed to provide the
  * request body, a block at a time.  The total size of the request
  * body is 'length'; the callback must ensure that it returns no more
- * than 'length' bytes in total. */
+ * than 'length' bytes in total.  If 'length' is set to -1, then the
+ * total size of the request is unknown by the called and chunked 
+ * tranfert will be used. */
 NEON_API void ne_set_request_body_provider(ne_request *req, ne_off_t
length,
 				  ne_provide_body provider, void
*userdata);
 
Index: ne_socket.h
===================================================================
--- ne_socket.h	(revision base)
+++ ne_socket.h	(revision new)
@@ -49,6 +49,12 @@
 /* ne_sock_addr represents an address object. */
 typedef struct ne_sock_addr_s ne_sock_addr;
 
+/* OS independant iovec structure*/
+struct ne_iovec_s {
+    void *base;
+    size_t len;
+};
+
 #ifndef NE_INET_ADDR_DEFINED
 typedef struct ne_inet_addr_s ne_inet_addr;
 #endif
@@ -171,6 +177,11 @@
  * on error. */
 NEON_API int ne_sock_fullwrite(ne_socket *sock, const char *data,
size_t count); 
 
+/* Write writes 'count' blocks described by 'vector' to the socket.
Guarantees to either
+ * write all the bytes or to fail.  Returns 0 on success, or NE_SOCK_*
+ * on error. */
+NEON_API int ne_sock_fullwritev(ne_socket *sock, const struct
ne_iovec_s *vector, int count); 
+
 /* Read an LF-terminated line into 'buffer', and NUL-terminate it.
  * At most 'len' bytes are read (including the NUL terminator).
  * Returns:




More information about the neon mailing list