add http_send_response function that is usable for non-blocking IO

Sun, 13 Nov 2022 10:57:38 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 13 Nov 2022 10:57:38 +0100
changeset 432
7c9137f9e7f9
parent 431
032b0ad35ee3
child 433
39fe86ae4db0

add http_send_response function that is usable for non-blocking IO

src/server/daemon/protocol.c file | annotate | diff | comparison | revisions
src/server/daemon/protocol.h file | annotate | diff | comparison | revisions
src/server/public/nsapi.h file | annotate | diff | comparison | revisions
--- a/src/server/daemon/protocol.c	Sun Nov 13 09:41:07 2022 +0100
+++ b/src/server/daemon/protocol.c	Sun Nov 13 10:57:38 2022 +0100
@@ -233,23 +233,23 @@
 }
 
 
-void add_http_status_line(sbuf_t *out, pool_handle_t *pool, Request *rq) {
-    sbuf_write(out, "HTTP/1.1 ", 9);
+void add_http_status_line(CxBuffer *out, pool_handle_t *pool, Request *rq) {
+    cxBufferWrite("HTTP/1.1 ", 1, 9, out);
 
-    char *status_code_str = pool_malloc(pool, 8);
+    char status_code_str[8];
     int sc_len = snprintf(status_code_str, 8, "%d ", rq->status_num);
-    sbuf_write(out, status_code_str, sc_len);
+    cxBufferWrite(status_code_str, 1, sc_len, out);
     
     char *scmsg = pblock_findkeyval(pb_key_status, rq->srvhdrs);
     if(scmsg == NULL) {
         scmsg = "OK";
     }
-    sbuf_write(out, scmsg, strlen(scmsg));
+    cxBufferWrite(scmsg, 1, strlen(scmsg), out);
 
-    sbuf_write(out, "\r\n", 2);
+    cxBufferWrite("\r\n", 1, 2, out);
 }
 
-void add_http_response_header(sbuf_t *out, Request *rq) {
+void add_http_response_header(CxBuffer *out, Request *rq) {
     pblock   *h = rq->srvhdrs;
     pb_entry *p;
 
@@ -275,53 +275,60 @@
                  * make first char uppercase and write the remaining chars
                  * unmodified to the buffer
                  */
-                sbuf_put(out, name[0]-32);
+                cxBufferPut(out, name[0]-32);
                 if(name_len > 1) {
-                    sbuf_write(out, name+1, name_len-1);
+                    cxBufferWrite(name+1, 1, name_len-1, out);
                 }
             } else {
                 // first char is already uppercase so just write the name
-                sbuf_write(out, name, name_len);
+                cxBufferWrite(name, 1, name_len, out);
             }
 
-            sbuf_write(out, ": ", 2);
-            sbuf_write(out, value, strlen(value));
-            sbuf_write(out, "\r\n", 2);
+            cxBufferWrite(": ", 1, 2, out);
+            cxBufferWrite(value, 1, strlen(value), out);
+            cxBufferWrite("\r\n", 1, 2, out);
 
             p = p->next;
         }
     }
 }
 
-int http_start_response(Session *sn, Request *rq) {
-    Connection *conn = ((NSAPISession*)sn)->connection;
+struct HttpResponseWriter {
+    Session *sn;
+    Request *rq;
+    CxBuffer buf;
+};
 
+HttpResponseWriter *http_create_response(Session *sn, Request *rq) {
+    HttpResponseWriter *writer = pool_malloc(sn->pool, sizeof(HttpResponseWriter));
+    if(!writer) {
+        return NULL;
+    }
+    if(cxBufferInit(&writer->buf, NULL, 512, pool_allocator(sn->pool), CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) {
+        pool_free(sn->pool, writer);
+        return NULL;
+    }
+    writer->sn = sn;
+    writer->rq = rq;
+    
     if(rq->status_num == -1) {
         protocol_status(sn, rq, 200, "OK");
     }
-
-    // set socket blocking
-    //int flags;
-    //flags = fcntl(fd, F_GETFL, 0);
-    //fcntl(fd, F_SETFL, flags ^ O_NONBLOCK);
-
-    // output buffer
-    sbuf_t *out = sbuf_new(512);
-
+    
     // add the http status line to the output buffer
-    add_http_status_line(out, sn->pool, rq);
+    add_http_status_line(&writer->buf, sn->pool, rq);
     
     // add date header
     struct tm mtms;
     struct tm *mtm = system_gmtime(&rq->req_start, &mtms);
     char date[HTTP_DATE_LEN + 1];
     strftime(date, HTTP_DATE_LEN, HTTP_DATE_FMT, mtm);
-    sbuf_write(out, "Date: ", 6);
-    sbuf_write(out, date, strlen(date));
-    sbuf_write(out, "\r\n", 2);
+    cxBufferWrite("Date: ", 1, 6, &writer->buf);
+    cxBufferWrite(date, 1, strlen(date), &writer->buf);
+    cxBufferWrite("\r\n", 1, 2, &writer->buf);
     
     // add server header
-    sbuf_write(out, "Server: webserver\r\n", 19);
+    cxBufferWrite("Server: webserver\r\n", 1, 19, &writer->buf);
     
     // check content length ans transfer encoding
     char *ctlen = pblock_findkeyval(pb_key_content_length, rq->srvhdrs);
@@ -353,30 +360,66 @@
     }
     
     // add header from rq->srvhdrs
-    add_http_response_header(out, rq);
+    add_http_response_header(&writer->buf, rq);
     
     // add connection header
     if(rq->rq_attr.keep_alive) {
-        sbuf_write(out, "Connection: keep-alive\r\n", 24);
+        cxBufferWrite("Connection: keep-alive\r\n", 1, 24, &writer->buf);
         pblock_kvinsert(pb_key_connection, "keep-alive", 10, rq->srvhdrs);
     } else {
-        sbuf_write(out, "Connection: close\r\n", 19);
+        cxBufferWrite("Connection: close\r\n", 1, 19, &writer->buf);
         pblock_kvinsert(pb_key_connection, "close", 5, rq->srvhdrs);
     }
-    
 
     // response header end
-    sbuf_write(out, "\r\n", 2);
+    cxBufferWrite("\r\n", 1, 2,& writer->buf);
+    
+    // reset pos (required for http_start_response_async)
+    writer->buf.pos = 0;
+    
+    return writer;
+}
 
+int http_send_response(HttpResponseWriter *writer) {
+    Connection *conn = ((NSAPISession*)writer->sn)->connection;
+    CxBuffer *buf = &writer->buf;
+    
     // flush buffer to the socket
-    conn->write(conn, out->ptr, out->length);
-    sbuf_free(out);
+    while(buf->pos < buf->size) {
+        int w = conn->write(conn, buf->space + buf->pos, buf->size - buf->pos);
+        if(w <= 0) {
+            if(conn->ssl) {
+                if(conn->ssl_error == SSL_ERROR_WANT_WRITE) {
+                    return 1;
+                }
+            } else {
+                if(errno == EWOULDBLOCK) {
+                    return 1;
+                }
+            }
+            return -1;
+        }
+        buf->pos += w;
+    }
      
-    rq->senthdrs = 1;
+    writer->rq->senthdrs = 1;
+    
+    cxBufferDestroy(buf);
+    pool_free(writer->sn->pool, writer);
     
     return 0;
 }
 
+
+int http_start_response(Session *sn, Request *rq) {
+    HttpResponseWriter *writer = http_create_response(sn, rq);
+    if(!writer) {
+        return 1;
+    }
+    
+    return http_send_response(writer);
+}
+
 int http_send_continue(Session *sn) {
     NSAPISession *s = (NSAPISession*)sn;
     cxstring msg = CX_STR("HTTP/1.1 100 Continue\r\n\r\n");
--- a/src/server/daemon/protocol.h	Sun Nov 13 09:41:07 2022 +0100
+++ b/src/server/daemon/protocol.h	Sun Nov 13 10:57:38 2022 +0100
@@ -33,6 +33,7 @@
 #include "../util/io.h"
 #include <sys/uio.h>
 #include "../util/strbuf.h"
+#include <cx/buffer.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -41,8 +42,8 @@
 void protocol_status(Session *sn, Request *rq, int n, const char *m);
 const char* protocol_status_message(int code);
 
-void add_http_status_line(sbuf_t *out, pool_handle_t *pool, Request *rq);
-void add_http_response_header(sbuf_t *out, Request *rq);
+void add_http_status_line(CxBuffer *out, pool_handle_t *pool, Request *rq);
+void add_http_response_header(CxBuffer *out, Request *rq);
 
 int http_start_response(Session *sn, Request *rq);
 
--- a/src/server/public/nsapi.h	Sun Nov 13 09:41:07 2022 +0100
+++ b/src/server/public/nsapi.h	Sun Nov 13 10:57:38 2022 +0100
@@ -700,6 +700,9 @@
 typedef struct ConfigNode          WSConfigNode;
 typedef enum WSConfigNodeType      WSConfigNodeType;
 
+// new
+typedef struct HttpResponseWriter  HttpResponseWriter;
+
 #ifndef PR_AF_INET
 typedef union PRNetAddr PRNetAddr;
 #endif
@@ -1511,6 +1514,16 @@
 
 int http_start_response(Session *sn, Request *rq);
 #define protocol_start_response http_start_response
+
+HttpResponseWriter *http_create_response(Session *sn, Request *rq);
+
+/*
+ * return: -1: error
+ *          0: finished
+ *          1: EWOULDBLOCK
+ */
+int http_send_response(HttpResponseWriter *writer);
+
 int request_header(char *name, char **value, Session *sn, Request *rq);
 
 char *http_uri2url(const char *prefix, const char *suffix);

mercurial