refactor HttpParser to support parsing of Http responses

Fri, 06 Feb 2026 14:06:04 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Fri, 06 Feb 2026 14:06:04 +0100
changeset 662
70fdf948b642
parent 661
a4e1ba59b733
child 663
bd116bd44926

refactor HttpParser to support parsing of Http responses

src/server/Makefile file | annotate | diff | comparison | revisions
src/server/daemon/httpparser.c file | annotate | diff | comparison | revisions
src/server/daemon/httpparser.h file | annotate | diff | comparison | revisions
src/server/daemon/httprequest.c file | annotate | diff | comparison | revisions
src/server/daemon/httprequest.h file | annotate | diff | comparison | revisions
src/server/daemon/sessionhandler.c file | annotate | diff | comparison | revisions
src/server/daemon/ws-fn.c file | annotate | diff | comparison | revisions
src/server/proxy/Makefile file | annotate | diff | comparison | revisions
src/server/proxy/httpclient.c file | annotate | diff | comparison | revisions
src/server/proxy/httpclient.h file | annotate | diff | comparison | revisions
src/server/proxy/objs.mk file | annotate | diff | comparison | revisions
src/server/safs/objs.mk file | annotate | diff | comparison | revisions
src/server/safs/proxy.c file | annotate | diff | comparison | revisions
src/server/safs/proxy.h file | annotate | diff | comparison | revisions
src/server/test/httpparser.c file | annotate | diff | comparison | revisions
src/server/test/httpparser.h file | annotate | diff | comparison | revisions
src/server/test/main.c file | annotate | diff | comparison | revisions
src/server/test/objs.mk file | annotate | diff | comparison | revisions
src/server/util/objs.mk file | annotate | diff | comparison | revisions
--- a/src/server/Makefile	Tue Feb 03 19:09:53 2026 +0100
+++ b/src/server/Makefile	Fri Feb 06 14:06:04 2026 +0100
@@ -44,6 +44,7 @@
 include webdav/objs.mk
 include daemon/objs.mk
 include config/objs.mk
+include proxy/objs.mk
 include admin/objs.mk
 include test/objs.mk
 
@@ -52,14 +53,15 @@
 include webdav/Makefile
 include daemon/Makefile
 include config/Makefile
+include proxy/Makefile
 include admin/Makefile
 include test/Makefile
 
-MAINOBJS = $(UTILOBJS) $(SAFOBJS) $(DAVOBJS) $(DAEMONOBJS) $(CONFOBJS) $(ADMINOBJS)
+MAINOBJS = $(UTILOBJS) $(SAFOBJS) $(DAVOBJS) $(DAEMONOBJS) $(CONFOBJS) $(PROXYOBJS) $(ADMINOBJS)
 
 TESTOBJS += $(MAINOBJS)
 
-OBJ_DIRS = daemon safs ucx util webdav config admin plugins test
+OBJ_DIRS = daemon safs ucx util webdav config proxy admin plugins test
 MK_OBJ_DIRS = $(OBJ_DIRS:%=$(OBJ_DIR)server/%)
 
 WS_CFLAGS = -I../ucx/
--- a/src/server/daemon/httpparser.c	Tue Feb 03 19:09:53 2026 +0100
+++ b/src/server/daemon/httpparser.c	Fri Feb 06 14:06:04 2026 +0100
@@ -35,11 +35,17 @@
 
 
 HttpParser* http_parser_new(HTTPRequest *request) {
+    return http_parser_new2(0, request->netbuf, request->headers);
+}
+
+HttpParser* http_parser_new2(int type, netbuf *netbuf, HeaderArray *headers) {
     HttpParser *parser = calloc(1, sizeof(HttpParser));
-    parser->request = request;
+    parser->type = type;
+    parser->netbuf = netbuf;
+    parser->headers = headers;
     
     parser->state = 0;
-    parser->start_line.ptr = (char*)request->netbuf->inbuf;
+    parser->start_line.ptr = (char*)netbuf->inbuf;
     parser->start_line.length = 0;
     
     parser->offset = 0;
@@ -51,6 +57,12 @@
     free(parser);
 }
 
+void http_parser_update_request(HttpParser *parser, HTTPRequest *request) {
+    request->method = parser->method;
+    request->uri = parser->uri;
+    request->httpv = parser->httpv;
+}
+
 int http_parser_process(HttpParser *parser) {
     switch(parser->state) {
         case 0: {
@@ -59,7 +71,16 @@
                 case 0: break;
                 default: return r;
             }
-            parse_request_line(parser);
+            if(parser->type == 0) {
+                if(parse_request_line(parser)) {
+                    return 2;
+                }
+            } else {
+                if(parse_response_line(parser)) {
+                    return 2;
+                }
+            }
+            
             parser->state++;
         }
         case 1: {
@@ -73,19 +94,27 @@
 }
 
 int http_parser_validate(HttpParser *parser) {
-    HTTPRequest *req = parser->request;
-    if(
-            !req->method.ptr || req->method.length == 0
-            || !req->uri.ptr || req->uri.length == 0
-            || !req->httpv.ptr || req->httpv.length == 0)
-    {
-        return 0;
+    if(parser->type == 0) {
+        if (!parser->method.ptr || parser->method.length == 0
+            || !parser->uri.ptr || parser->uri.length == 0
+            || !parser->httpv.ptr || parser->httpv.length == 0)
+        {
+            return 0;
+        }
+    } else {
+        if (!parser->httpv.ptr || parser->httpv.length == 0
+            || !parser->msg.ptr || parser->msg.length == 0
+            || parser->status == 0)
+        {
+            return 0;
+        }
     }
+    
     return 1;
 }
 
 int get_start_line(HttpParser *parser) {
-    netbuf *buf = parser->request->netbuf;
+    netbuf *buf = parser->netbuf;
     while(buf->pos < buf->cursize) {
         unsigned char c = buf->inbuf[buf->pos];
         if(c == '\n') {
@@ -115,7 +144,7 @@
 }
 
 int http_parser_parse_header(HttpParser *parser) {
-    netbuf *buf = parser->request->netbuf;
+    netbuf *buf = parser->netbuf;
 
     parser->offset = buf->pos; // line offset
     parser->name.ptr = NULL;
@@ -154,8 +183,8 @@
                         parser->value.ptr = "";
                     }
                     // add header
-                    header_add(
-                            parser->request->headers,
+                    header_array_add(
+                            parser->headers,
                             parser->name,
                             parser->value);
                 } else {
@@ -174,54 +203,117 @@
 
 int parse_request_line(HttpParser *parser) {
     cxmutstr line = parser->start_line;
-    parser->request->request_line = line;
     
-    /*
-     * parse method, url and http version
-     */
+    // parse method, url and http version
     
     int i = 0;
     int ns = 0;
 
-    parser->request->method.ptr = line.ptr;
+    parser->method.ptr = line.ptr;
     for(;i<line.length;i++) {
         if(!ns && line.ptr[i] == ' ') {
             ns = 1;
-            parser->request->method.length = i;
+            parser->method.length = i;
         } else if(ns) {
             if(line.ptr[i] != ' ') {
                 break;
             }
         }
     }
+    if(i == line.length) {
+        return 1;
+    }
 
-    parser->request->uri.ptr = line.ptr + i;
+    parser->uri.ptr = line.ptr + i;
     ns = 0;
     int s = i;
     for(;i<line.length;i++) {
         if(!ns && isspace(line.ptr[i])) {
             ns = 1;
-            parser->request->uri.length = i - s;
+            parser->uri.length = i - s;
         } else if(ns) {
             if(line.ptr[i] > 32) {
                 break;
             }
         }
     }
+    if(i == line.length) {
+        return 1;
+    }
 
-    parser->request->httpv.ptr = line.ptr + i;
+    parser->httpv.ptr = line.ptr + i;
     ns = 0;
     s = i;
     for(;i<line.length;i++) {
         if(!ns && isspace(line.ptr[i])) {
             ns = 1;
-            parser->request->httpv.length = i - s;
+            parser->httpv.length = i - s;
+        } else if(ns) {
+            if(line.ptr[i] > 32) {
+                return 1; // non-whitespace char after httpv
+            }
+        }
+    }
+
+    return 0;
+}
+
+int parse_response_line(HttpParser *parser) {
+    cxmutstr line = parser->start_line;
+    
+    // parse http version, status num, status message
+    
+    int i = 0;
+    int ns = 0;
+    
+    parser->httpv.ptr = line.ptr;
+    for(;i<line.length;i++) {
+        if(!ns && line.ptr[i] == ' ') {
+            ns = 1;
+            parser->httpv.length = i;
+        } else if(ns) {
+            if(line.ptr[i] != ' ') {
+                break;
+            }
+        }
+    }
+    
+    ns = 0;
+    int s = i;
+    cxmutstr num_str;
+    num_str.ptr = line.ptr + i;
+    num_str.length = 0;
+    for(;i<line.length;i++) {
+        if(!ns && isspace(line.ptr[i])) {
+            ns = 1;
+            num_str.length = i - s;
         } else if(ns) {
             if(line.ptr[i] > 32) {
                 break;
             }
         }
     }
-
+    
+    if(num_str.length != 3) {
+        return 1;
+    }
+    if(cx_strtoi(num_str, &parser->status, 10)) {
+        return 1;
+    }
+    
+    parser->msg.ptr = line.ptr + i;
+    ns = 1;
+    s = i;
+    for(;i<line.length;i++) {
+        char c = line.ptr[i];
+        if(ns && c < 33) {
+            parser->msg.length = i - s;
+            ns = 0;
+        } else {
+            ns = 1;
+        }
+    }
+    
     return 0;
 }
+
--- a/src/server/daemon/httpparser.h	Tue Feb 03 19:09:53 2026 +0100
+++ b/src/server/daemon/httpparser.h	Fri Feb 06 14:06:04 2026 +0100
@@ -37,19 +37,37 @@
 extern "C" {
 #endif
 
-/*
- * http parser states
- *
- * 0: start line
- * 1: header
- * 2: finish
- */
-
+  
 typedef struct _http_parser {
-    HTTPRequest *request;
-
+    /*
+     * http parser type
+     * 
+     * 0: request
+     * 1: response
+     */
+    int type;
+    
+    /*
+     * http parser states
+     *
+     * 0: start line
+     * 1: header
+     * 2: finish
+     */
     int state;
+    
+    /*
+     * first line (request/response line)
+     */
     cxmutstr start_line;
+    
+    netbuf *netbuf;
+    HeaderArray *headers;
+    cxmutstr method;
+    cxmutstr uri;
+    cxmutstr httpv;
+    cxmutstr msg;
+    int      status;
 
     /* local parser variables */
     int wl;       /* only white space */
@@ -62,9 +80,15 @@
 } HttpParser;
 
 HttpParser* http_parser_new(HTTPRequest *request);
+HttpParser* http_parser_new2(int type, netbuf *netbuf, HeaderArray *headers);
 void http_parser_free(HttpParser *parser);
 
 /*
+ * updates method, uri and httpv in an HTTPRequest object
+ */
+void http_parser_update_request(HttpParser *parser, HTTPRequest *request);
+
+/*
  * process http parsing
  *
  * return
@@ -80,7 +104,7 @@
 int http_parser_parse_header(HttpParser *parser);
 
 int parse_request_line(HttpParser *parser);
-
+int parse_response_line(HttpParser *parser);
 
 
 #ifdef	__cplusplus
--- a/src/server/daemon/httprequest.c	Tue Feb 03 19:09:53 2026 +0100
+++ b/src/server/daemon/httprequest.c	Fri Feb 06 14:06:04 2026 +0100
@@ -471,15 +471,26 @@
 }
 
 
+HeaderArray* header_array_create(void) {
+    HeaderArray *array = malloc(sizeof(HeaderArray));
+    if(!array) {
+        return NULL;
+    }
+    array->next = NULL;
+    array->len = 0;
+    array->alloc = 16;
+    array->headers = calloc(16, sizeof(Header));
+    if(array->headers) {
+        free(array);
+        return NULL;
+    }
+    return array;
+}
 
-void header_add(HeaderArray *hd, cxmutstr name, cxmutstr value) {
+void header_array_add(HeaderArray *hd, cxmutstr name, cxmutstr value) {
     while(hd->len >= hd->alloc) {
         if(hd->next == NULL) {
-            HeaderArray *block = malloc(sizeof(HeaderArray));
-            block->next = NULL;
-            block->len = 0;
-            block->headers = calloc(16, sizeof(Header));
-            block->alloc = 16;
+            HeaderArray *block = header_array_create();
             hd->next = block;
         }
         hd = hd->next;
--- a/src/server/daemon/httprequest.h	Tue Feb 03 19:09:53 2026 +0100
+++ b/src/server/daemon/httprequest.h	Fri Feb 06 14:06:04 2026 +0100
@@ -93,7 +93,8 @@
         pool_handle_t *pool);
 
 
-void header_add(HeaderArray *hd, cxmutstr name, cxmutstr value);
+HeaderArray* header_array_create(void);
+void header_array_add(HeaderArray *hd, cxmutstr name, cxmutstr value);
 void header_array_free(HeaderArray *hd);
 
 int nsapi_handle_request(NSAPISession *sn, NSAPIRequest *rq);
--- a/src/server/daemon/sessionhandler.c	Tue Feb 03 19:09:53 2026 +0100
+++ b/src/server/daemon/sessionhandler.c	Fri Feb 06 14:06:04 2026 +0100
@@ -188,6 +188,7 @@
         
         if(!err) {
             if(http_parser_validate(parser)) {
+                http_parser_update_request(parser, request);
                 // process request
                 r = handle_request(request, NULL, NULL); // TODO: use correct thread pool
             } else {
@@ -506,6 +507,7 @@
         event->finish = evt_request_error;
         return 0;
     }
+    http_parser_update_request(parser, request);
     
     /*
      * process request
--- a/src/server/daemon/ws-fn.c	Tue Feb 03 19:09:53 2026 +0100
+++ b/src/server/daemon/ws-fn.c	Fri Feb 06 14:06:04 2026 +0100
@@ -39,6 +39,7 @@
 #include "../safs/addlog.h"
 #include "../safs/cgi.h"
 #include "../safs/ldap.h"
+#include "../safs/proxy.h"
 #include "../webdav/webdav.h"
 
 #include "../admin/admin.h"
@@ -77,5 +78,6 @@
     { "ldap-search", service_ldap_search, NULL, NULL, 0},
     { "webdav-init", webdav_init, NULL, NULL, 0},
     { "webdav-service", webdav_service, NULL, NULL, 0},
+    { "reverse-proxy-service", http_reverse_proxy_service, NULL, NULL, 0},
     {NULL, NULL, NULL, NULL, 0}
 };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/proxy/Makefile	Fri Feb 06 14:06:04 2026 +0100
@@ -0,0 +1,33 @@
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2026 Olaf Wintermann. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#   1. Redistributions of source code must retain the above copyright
+#      notice, this list of conditions and the following disclaimer.
+#
+#   2. Redistributions in binary form must reproduce the above copyright
+#      notice, this list of conditions and the following disclaimer in the
+#      documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+PROXY_CFLAGS = 
+
+$(PROXY_OBJPRE)%.o: proxy/%.c
+	$(CC) -o $@ -c $(WS_CFLAGS) $(PROXY_CFLAGS) $(CFLAGS) $<
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/proxy/httpclient.c	Fri Feb 06 14:06:04 2026 +0100
@@ -0,0 +1,5 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/cFiles/file.c to edit this template
+ */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/proxy/httpclient.h	Fri Feb 06 14:06:04 2026 +0100
@@ -0,0 +1,46 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PROXY_HTTPCLIENT_H
+#define PROXY_HTTPCLIENT_H
+
+#include "../public/nsapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PROXY_HTTPCLIENT_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/proxy/objs.mk	Fri Feb 06 14:06:04 2026 +0100
@@ -0,0 +1,37 @@
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2026 Olaf Wintermann. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#   1. Redistributions of source code must retain the above copyright notice,
+#      this list of conditions and the following disclaimer.
+#
+#   2. Redistributions in binary form must reproduce the above copyright
+#      notice, this list of conditions and the following disclaimer in the
+#      documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+PROXY_SRC_DIR = server/proxy/
+
+PROXY_OBJPRE = $(OBJ_DIR)$(PROXY_SRC_DIR)
+
+PROXYOBJ = httpclient.o
+
+PROXYOBJS = $(PROXYOBJ:%=$(PROXY_OBJPRE)%)
+PROXYSOURCE = $(PROXYOBJ:%.o=proxy/%.c)
+
--- a/src/server/safs/objs.mk	Tue Feb 03 19:09:53 2026 +0100
+++ b/src/server/safs/objs.mk	Fri Feb 06 14:06:04 2026 +0100
@@ -42,6 +42,7 @@
 SAFOBJ += cgi.o
 SAFOBJ += cgiutils.o
 SAFOBJ += ldap.o
+SAFOBJ += proxy.o
 
 SAFOBJS = $(SAFOBJ:%=$(SAFS_OBJPRE)%)
 SAFSOURCE = $(SAFOBJ:%.o=safs/%.c)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/safs/proxy.c	Fri Feb 06 14:06:04 2026 +0100
@@ -0,0 +1,37 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "proxy.h"
+
+#include "../util/pblock.h"
+
+int http_reverse_proxy_service(pblock *param, Session *sn, Request *rq) {
+    
+    
+    return REQ_ABORTED;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/safs/proxy.h	Fri Feb 06 14:06:04 2026 +0100
@@ -0,0 +1,46 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SAF_PROXY_H
+#define SAF_PROXY_H
+
+#include "../public/nsapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int http_reverse_proxy_service(pblock *param, Session *sn, Request *rq);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SAF_PROXY_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/test/httpparser.c	Fri Feb 06 14:06:04 2026 +0100
@@ -0,0 +1,136 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "httpparser.h"
+
+#include "../daemon/httpparser.h"
+
+CX_TEST(test_parse_request_line) {
+    CX_TEST_DO {
+        HttpParser parser;
+        memset(&parser, 0, sizeof(HttpParser));
+        parser.start_line = cx_mutstr("GET / HTTP/1.1\r\n");
+        
+        int ret1 = parse_request_line(&parser);
+        CX_TEST_ASSERT(!ret1);
+        CX_TEST_ASSERT(!cx_strcmp(parser.method, "GET"));
+        CX_TEST_ASSERT(!cx_strcmp(parser.uri, "/"));
+        CX_TEST_ASSERT(!cx_strcmp(parser.httpv, "HTTP/1.1"));
+        
+        memset(&parser, 0, sizeof(HttpParser));
+        parser.start_line = cx_mutstr("POST /longer/url/test/path/ HTTP/1.0\r\n");
+        int ret2 = parse_request_line(&parser);
+        CX_TEST_ASSERT(!ret2);
+        CX_TEST_ASSERT(!cx_strcmp(parser.method, "POST"));
+        CX_TEST_ASSERT(!cx_strcmp(parser.uri, "/longer/url/test/path/"));
+        CX_TEST_ASSERT(!cx_strcmp(parser.httpv, "HTTP/1.0"));
+        
+        memset(&parser, 0, sizeof(HttpParser));
+        parser.start_line = cx_mutstr("G / v\r\n");
+        int ret3 = parse_request_line(&parser);
+        CX_TEST_ASSERT(!ret3);
+        CX_TEST_ASSERT(!cx_strcmp(parser.method, "G"));
+        CX_TEST_ASSERT(!cx_strcmp(parser.uri, "/"));
+        CX_TEST_ASSERT(!cx_strcmp(parser.httpv, "v"));
+        
+        memset(&parser, 0, sizeof(HttpParser));
+        parser.start_line = cx_mutstr("HEAD   /space      HTTP/0.9    \r\n");
+        int ret4 = parse_request_line(&parser);
+        CX_TEST_ASSERT(!ret4);
+        CX_TEST_ASSERT(!cx_strcmp(parser.method, "HEAD"));
+        CX_TEST_ASSERT(!cx_strcmp(parser.uri, "/space"));
+        CX_TEST_ASSERT(!cx_strcmp(parser.httpv, "HTTP/0.9"));
+        
+        // unix-style line break tests
+        memset(&parser, 0, sizeof(HttpParser));
+        parser.start_line = cx_mutstr("PROPFIND /collection/ HTTP/1.1\n");
+        int uret1 = parse_request_line(&parser);
+        CX_TEST_ASSERT(!uret1);
+        CX_TEST_ASSERT(!cx_strcmp(parser.method, "PROPFIND"));
+        CX_TEST_ASSERT(!cx_strcmp(parser.uri, "/collection/"));
+        CX_TEST_ASSERT(!cx_strcmp(parser.httpv, "HTTP/1.1"));
+        
+        // negative tests
+        parser.start_line = cx_mutstr("\r\n");
+        int nret1 = parse_request_line(&parser);
+        CX_TEST_ASSERT(nret1);
+        
+        parser.start_line = cx_mutstr("GET\r\n");
+        int nret2 = parse_request_line(&parser);
+        CX_TEST_ASSERT(nret2);
+        
+        parser.start_line = cx_mutstr("GET /test\r\n");
+        int nret3 = parse_request_line(&parser);
+        CX_TEST_ASSERT(nret3);
+        
+        parser.start_line = cx_mutstr("GET /test   \r\n");
+        int nret4 = parse_request_line(&parser);
+        CX_TEST_ASSERT(nret4);
+        
+        parser.start_line = cx_mutstr("/test HTTP/1.1\r\n");
+        int nret5 = parse_request_line(&parser);
+        CX_TEST_ASSERT(nret5);
+        
+        parser.start_line = cx_mutstr("GET /uri HTTP/1.1 test\r\n");
+        int nret6 = parse_request_line(&parser);
+        CX_TEST_ASSERT(nret6);
+    }
+}
+
+CX_TEST(test_parse_response_line) {
+    CX_TEST_DO {
+        HttpParser parser;
+        memset(&parser, 0, sizeof(HttpParser));
+        parser.start_line = cx_mutstr("HTTP/1.1 200 OK\r\n");
+        parser.type = 1;
+        
+        int ret1 = parse_response_line(&parser);
+        CX_TEST_ASSERT(!ret1);
+        CX_TEST_ASSERT(!cx_strcmp(parser.httpv, "HTTP/1.1"));
+        CX_TEST_ASSERT(!cx_strcmp(parser.msg, "OK"));
+        CX_TEST_ASSERT(parser.status == 200);
+        
+        memset(&parser, 0, sizeof(HttpParser));
+        parser.start_line = cx_mutstr("HTTP/1.0 201 Created\r\n");
+        parser.type = 1;
+        int ret2 = parse_response_line(&parser);
+        CX_TEST_ASSERT(!ret2);
+        CX_TEST_ASSERT(!cx_strcmp(parser.httpv, "HTTP/1.0"));
+        CX_TEST_ASSERT(!cx_strcmp(parser.msg, "Created"));
+        CX_TEST_ASSERT(parser.status == 201);
+        
+        memset(&parser, 0, sizeof(HttpParser));
+        parser.start_line = cx_mutstr("HTTP/0.9    204    No Content\r\n");
+        parser.type = 1;
+        int ret3 = parse_response_line(&parser);
+        CX_TEST_ASSERT(!ret3);
+        CX_TEST_ASSERT(!cx_strcmp(parser.httpv, "HTTP/0.9"));
+        CX_TEST_ASSERT(!cx_strcmp(parser.msg, "No Content"));
+        CX_TEST_ASSERT(parser.status == 204);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/test/httpparser.h	Fri Feb 06 14:06:04 2026 +0100
@@ -0,0 +1,47 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TEST_HTTPPARSER_H
+#define TEST_HTTPPARSER_H
+
+#include "test.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+CX_TEST(test_parse_request_line);
+CX_TEST(test_parse_response_line);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TEST_HTTPPARSER_H */
+
--- a/src/server/test/main.c	Tue Feb 03 19:09:53 2026 +0100
+++ b/src/server/test/main.c	Fri Feb 06 14:06:04 2026 +0100
@@ -42,6 +42,7 @@
 
 #include "test.h"
 
+#include "httpparser.h"
 #include "vfs.h"
 #include "writer.h"
 #include "xml.h"
@@ -82,6 +83,10 @@
     cx_test_register(suite, test_util_uri_escape_latin);
     cx_test_register(suite, test_util_uri_escape_kanji);
     
+    // httpparser tests
+    cx_test_register(suite, test_parse_request_line);
+    cx_test_register(suite, test_parse_response_line);
+    
     // event tests
     cx_test_register(suite, test_evhandler_create);
     cx_test_register(suite, test_event_send);
--- a/src/server/test/objs.mk	Tue Feb 03 19:09:53 2026 +0100
+++ b/src/server/test/objs.mk	Fri Feb 06 14:06:04 2026 +0100
@@ -32,6 +32,7 @@
 
 TESTOBJ  = test.o
 TESTOBJ += main.o
+TESTOBJ += httpparser.o
 TESTOBJ += testutils.o
 TESTOBJ += webdav.o
 TESTOBJ += vfs.o
--- a/src/server/util/objs.mk	Tue Feb 03 19:09:53 2026 +0100
+++ b/src/server/util/objs.mk	Fri Feb 06 14:06:04 2026 +0100
@@ -46,13 +46,8 @@
 UTILOBJ += writer.o
 UTILOBJ += libxattr.o
 UTILOBJ += hashing.o
+UTILOBJ += pblock.o
+UTILOBJ += uri.o
 
 UTILOBJS = $(UTILOBJ:%=$(UTIL_OBJPRE)%)
 UTILSOURCE = $(UTILOBJ:%.o=util/%.c)
-
-# add cpp files
-UTILOBJS += $(UTIL_OBJPRE)pblock.o
-UTILOBJS += $(UTIL_OBJPRE)uri.o
-
-UTILSOURCE += util/pblock.cpp
-UTILSOURCE += util/uri.cpp

mercurial