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