change net_write to attempt to write all bytes, improve error handling

Sat, 30 Mar 2024 12:35:09 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 30 Mar 2024 12:35:09 +0100
changeset 513
9a49c245a49c
parent 511
a5a142fea2ae
child 514
922bfe380c8e

change net_write to attempt to write all bytes, improve error handling

src/server/daemon/protocol.c file | annotate | diff | comparison | revisions
src/server/test/io.c file | annotate | diff | comparison | revisions
src/server/test/testutils.c file | annotate | diff | comparison | revisions
src/server/util/io.c file | annotate | diff | comparison | revisions
src/server/util/io.h file | annotate | diff | comparison | revisions
src/server/webdav/multistatus.c file | annotate | diff | comparison | revisions
--- a/src/server/daemon/protocol.c	Tue Sep 12 18:08:11 2023 +0200
+++ b/src/server/daemon/protocol.c	Sat Mar 30 12:35:09 2024 +0100
@@ -354,8 +354,7 @@
         }
         
         // set stream property
-        HttpStream *stream = (HttpStream*)sn->csd; // TODO: make this typesafe
-        stream->chunked_enc = 1;
+        httpstream_enable_chunked_write(sn->csd);
         rq->rq_attr.chunked = 1;
     }
     
--- a/src/server/test/io.c	Tue Sep 12 18:08:11 2023 +0200
+++ b/src/server/test/io.c	Sat Mar 30 12:35:09 2024 +0100
@@ -445,6 +445,7 @@
     size_t msglen = strlen(msg);
     
     st->max_write = 1; // limit the test stream max write size
+    io_set_max_writes(1);
     
     // only 1 byte of the header is written, 0 bytes of msg
     ssize_t w = net_write(http, msg, msglen);
@@ -493,6 +494,7 @@
     
     // limit first write to 3 to only write the header
     st->max_write = 3;
+    io_set_max_writes(1);
     
     ssize_t w = net_write(http, msg, msglen);
     
@@ -535,6 +537,7 @@
     TestIOStream *st = testutil_iostream(2048, TRUE);
     IOStream *http = httpstream_new(sn->pool, (IOStream*)st);
     httpstream_enable_chunked_write(http);
+    io_set_max_writes(1);
     
     UCX_TEST_BEGIN;
     
@@ -595,6 +598,7 @@
     TestIOStream *st = testutil_iostream(2048, TRUE);
     IOStream *http = httpstream_new(sn->pool, (IOStream*)st);
     httpstream_enable_chunked_write(http);
+    io_set_max_writes(1);
     
     UCX_TEST_BEGIN;
     
@@ -654,6 +658,7 @@
     TestIOStream *st = testutil_iostream(2048, TRUE);
     IOStream *http = httpstream_new(sn->pool, (IOStream*)st);
     httpstream_enable_chunked_write(http);
+    io_set_max_writes(1);
     
     UCX_TEST_BEGIN;
     
@@ -700,6 +705,7 @@
     TestIOStream *st = testutil_iostream(2048, TRUE);
     IOStream *http = httpstream_new(sn->pool, (IOStream*)st);
     httpstream_enable_chunked_write(http);
+    io_set_max_writes(1);
     
     UCX_TEST_BEGIN;
     
--- a/src/server/test/testutils.c	Tue Sep 12 18:08:11 2023 +0200
+++ b/src/server/test/testutils.c	Sat Mar 30 12:35:09 2024 +0100
@@ -184,6 +184,7 @@
     stream->io.st.writev = test_io_writev;
     stream->io.st.close = test_io_close;
     stream->io.st.finish = test_io_finish;
+    stream->io.st.type = IO_STREAM_TYPE_HTTP;
     
     return stream;
 }
--- a/src/server/util/io.c	Tue Sep 12 18:08:11 2023 +0200
+++ b/src/server/util/io.c	Sat Mar 30 12:35:09 2024 +0100
@@ -78,6 +78,7 @@
     NULL,
     (io_setmode_f)net_sys_setmode,
     (io_poll_f)net_sys_poll,
+    0,
     0
 };
 
@@ -90,7 +91,8 @@
     (io_finish_f)net_http_finish,
     (io_setmode_f)net_http_setmode,
     (io_poll_f)net_http_poll,
-    0
+    0,
+    IO_STREAM_TYPE_HTTP
 };
 
 IOStream ssl_io_funcs = {
@@ -102,9 +104,15 @@
     (io_finish_f)net_ssl_finish,
     (io_setmode_f)net_ssl_setmode,
     (io_poll_f)net_ssl_poll,
-    0
+    0,
+    IO_STREAM_TYPE_SSL
 };
 
+static int net_write_max_attempts = 16384;
+
+void io_set_max_writes(int n) {
+    net_write_max_attempts = 1;
+}
 
 /*
  * Sysstream implementation
@@ -300,7 +308,7 @@
 }
 
 int httpstream_enable_chunked_write(IOStream *st) {
-    if(st->write != (io_write_f)net_http_write) {
+    if(st->type != IO_STREAM_TYPE_HTTP) {
         log_ereport(LOG_FAILURE, "%s", "httpstream_enable_chunked_write: IOStream is not an HttpStream");
         return 1;
     }
@@ -467,7 +475,7 @@
         st->written += ret_w;
         if(ret_w == 0) {
             st->st.io_errno = EWOULDBLOCK; // not sure if this is really correct
-            ret_w = -1;
+            //ret_w = -1;
         }
         return ret_w;
     }
@@ -876,11 +884,25 @@
 }
 
 ssize_t net_write(SYS_NETFD fd, const void *buf, size_t nbytes) {
-    ssize_t r = ((IOStream*)fd)->write(fd, buf, nbytes);
-    if(r < 0) {
+    size_t w = 0;
+    size_t remaining = nbytes;
+    const char *cbuf = buf;
+    ssize_t r = 0;
+    int attempts = 0;
+    while(w < nbytes && attempts < net_write_max_attempts) {
+        r = ((IOStream*)fd)->write(fd, cbuf, remaining);
+        if(r <= 0) {
+            break;
+        }
+        w += r;
+        cbuf += r;
+        remaining -= r;
+        attempts++;
+    }
+    if(r < 0 && w == 0) {
         return IO_ERROR;
     }  
-    return r;
+    return w;
 }
 
 ssize_t net_writev(SYS_NETFD fd, struct iovec *iovec, int iovcnt) {
--- a/src/server/util/io.h	Tue Sep 12 18:08:11 2023 +0200
+++ b/src/server/util/io.h	Sat Mar 30 12:35:09 2024 +0100
@@ -55,6 +55,9 @@
 #define IO_POLL_IN          1
 #define IO_POLL_OUT         2
     
+#define IO_STREAM_TYPE_HTTP 0x48545450
+#define IO_STREAM_TYPE_SSL  0x53534C
+    
 typedef struct IOStream     IOStream;
 typedef struct Sysstream    Sysstream;
 typedef struct HttpStream   HttpStream;
@@ -78,6 +81,7 @@
     io_setmode_f  setmode;
     io_poll_f     poll;
     int           io_errno;
+    unsigned int  type;
 };
 
 struct Sysstream {
@@ -180,7 +184,7 @@
     int      error;
 } SSLStream;
 
-
+void io_set_max_writes(int n);
 
 /* system stream */
 IOStream* Sysstream_new(pool_handle_t *pool, SYS_SOCKET fd);
--- a/src/server/webdav/multistatus.c	Tue Sep 12 18:08:11 2023 +0200
+++ b/src/server/webdav/multistatus.c	Sat Mar 30 12:35:09 2024 +0100
@@ -348,7 +348,9 @@
     
     // start http response
     protocol_status(ms->sn, ms->rq, 207, NULL);
-    protocol_start_response(ms->sn, ms->rq);
+    if(protocol_start_response(ms->sn, ms->rq)) {
+        return 1;
+    }
     
     char buffer[MULTISTATUS_BUFFER_LENGTH];
     // create a writer, that flushes the buffer when it is filled

mercurial