add test for webdav_propfind() that checks if the response is valid xml webdav

Sat, 18 Jan 2020 16:31:52 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 18 Jan 2020 16:31:52 +0100
branch
webdav
changeset 233
c5985d2fc19a
parent 232
499711b2a970
child 234
f30740c3aafb

add test for webdav_propfind() that checks if the response is valid xml

src/server/test/main.c file | annotate | diff | comparison | revisions
src/server/test/testutils.c file | annotate | diff | comparison | revisions
src/server/test/testutils.h file | annotate | diff | comparison | revisions
src/server/test/webdav.c file | annotate | diff | comparison | revisions
src/server/test/webdav.h file | annotate | diff | comparison | revisions
src/server/webdav/multistatus.c file | annotate | diff | comparison | revisions
src/server/webdav/webdav.c file | annotate | diff | comparison | revisions
src/server/webdav/xml.c file | annotate | diff | comparison | revisions
--- a/src/server/test/main.c	Sat Jan 18 13:48:59 2020 +0100
+++ b/src/server/test/main.c	Sat Jan 18 16:31:52 2020 +0100
@@ -94,6 +94,9 @@
     ucx_test_register(suite, test_webdav_op_propfind_begin);
     ucx_test_register(suite, test_webdav_op_propfind_children);
     
+    // webdav methods
+    ucx_test_register(suite, test_webdav_propfind);
+    
     // run tests
     ucx_test_run(suite, stdout);
     fflush(stdout);
--- a/src/server/test/testutils.c	Sat Jan 18 13:48:59 2020 +0100
+++ b/src/server/test/testutils.c	Sat Jan 18 16:31:52 2020 +0100
@@ -41,6 +41,7 @@
 Session* testutil_session(void) {
     pool_handle_t *pool = pool_create();
     NSAPISession *sn = nsapisession_create(pool);
+    sn->connection = testutil_dummy_connection(pool);
     
     return &sn->sn;
 }
@@ -75,6 +76,28 @@
     return &rq->rq;
 }
 
+static int dummyconn_read(Connection *conn, void *buf, int len) {
+    return len;
+}
+
+static int dummyconn_write(Connection *conn, const void *buf, int len) {
+    return len;
+}
+
+static void dummyconn_close(Connection *conn) {
+    
+}
+
+
+Connection* testutil_dummy_connection(pool_handle_t *pool) {
+    Connection *conn = pool_malloc(pool, sizeof(Connection));
+    ZERO(conn, sizeof(Connection));
+    conn->read = dummyconn_read;
+    conn->write = dummyconn_write;
+    conn->close = dummyconn_close; 
+    return conn;
+}
+
 void testutil_request_body(Session *sn, Request *rq, const char *body, size_t len) {
     sstr_t cl = ucx_sprintf("%d", (int)len);
     pblock_nvreplace("content-length", cl.ptr, rq->headers);
--- a/src/server/test/testutils.h	Sat Jan 18 13:48:59 2020 +0100
+++ b/src/server/test/testutils.h	Sat Jan 18 16:31:52 2020 +0100
@@ -48,6 +48,8 @@
 
 Request* testutil_request(pool_handle_t *pool, const char *method, const char *uri);
 
+Connection* testutil_dummy_connection(pool_handle_t *pool);
+
 void testutil_request_body(Session *sn, Request *rq, const char *body, size_t len);
 
 void testutil_destroy_session(Session *sn);
--- a/src/server/test/webdav.c	Sat Jan 18 13:48:59 2020 +0100
+++ b/src/server/test/webdav.c	Sat Jan 18 16:31:52 2020 +0100
@@ -975,3 +975,34 @@
     UCX_TEST_END;
     testutil_destroy_session(sn);
 }
+
+UCX_TEST(test_webdav_propfind) {
+    Session *sn = testutil_session();
+    Request *rq = testutil_request(sn->pool, "PROPFIND", "/");
+    
+    pblock_nvinsert("path", "/", rq->vars);
+    pblock_nvinsert("uri", "/", rq->reqpb);
+    
+    
+    TestIOStream *st = testutil_iostream(2048, TRUE);
+    sn->csd = st;
+    
+    testutil_request_body(sn, rq, TEST_PROPFIND1, strlen(TEST_PROPFIND1));
+    
+    UCX_TEST_BEGIN;
+    
+    pblock *pb = pblock_create_pool(sn->pool, 4);
+    
+    int ret = webdav_propfind(pb, sn, rq);
+    
+    UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (1) failed");
+    
+    xmlDoc *doc = xmlReadMemory(
+            st->buf->space, st->buf->size, NULL, NULL, 0);
+    UCX_TEST_ASSERT(doc, "response is not valid xml");
+    
+    //printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space);
+    
+    UCX_TEST_END;
+    testutil_destroy_session(sn);
+}
--- a/src/server/test/webdav.h	Sat Jan 18 13:48:59 2020 +0100
+++ b/src/server/test/webdav.h	Sat Jan 18 16:31:52 2020 +0100
@@ -56,6 +56,8 @@
 UCX_TEST(test_webdav_op_propfind_begin);
 UCX_TEST(test_webdav_op_propfind_children);
 
+UCX_TEST(test_webdav_propfind);
+
 /* --------------------------- PROPFIND --------------------------- */
 
 #define TEST_PROPFIND1 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \
--- a/src/server/webdav/multistatus.c	Sat Jan 18 13:48:59 2020 +0100
+++ b/src/server/webdav/multistatus.c	Sat Jan 18 16:31:52 2020 +0100
@@ -30,13 +30,15 @@
 #include <stdlib.h>
 
 #include "../daemon/session.h"
+#include "../daemon/protocol.h"
 #include "../util/platform.h"
 
 #include <ucx/string.h>
 
-#include "operation.h"
+#include "multistatus.h"
 
-#include "multistatus.h"
+#include "operation.h"
+#include "xml.h"
 
 #define MULTISTATUS_BUFFER_LENGTH 2048
 
@@ -141,7 +143,10 @@
         switch(property->vtype) {
             case WS_VALUE_NO_TYPE: break;
             case WS_VALUE_XML_NODE: {
-                // TODO
+                wsxml_write_nodes_without_nsdef(
+                        ms->sn->pool,
+                        out,
+                        property->value.node);
                 break;
             }
             case WS_VALUE_XML_DATA: {
@@ -163,13 +168,13 @@
         }
         
         // end tag
-        writer_putc(out, '<');
+        writer_puts(out, S("</"));
         writer_puts(out, sstr((char*)property->namespace->prefix));
         writer_putc(out, ':');
         writer_puts(out, sstr((char*)property->name));
         writer_putc(out, '>');
     } else {
-        writer_putc(out, '/>');
+        writer_puts(out, S("/>"));
     }
     
     return out->error;
@@ -179,42 +184,79 @@
     writer_puts(out, S(" <D:response>\n"
                        "  <D:href>"));
     writer_puts(out, sstr(rp->resource.href));
-    writer_puts(out, S("</href>\n"));
+    writer_puts(out, S("</D:href>\n"));
     
     if(rp->plist_begin) {
-        writer_puts(out, S("  <D:propstat>"
+        writer_puts(out, S("  <D:propstat>\n"
                            "   <D:prop>\n"));
         // send properties
         PropertyOkList *p = rp->plist_begin;
         while(p) {
+            writer_puts(out, S("    "));
             if(send_property(ms, p->property, p->nsdef, TRUE, out)) {
                 return out->error;
             }
+            writer_puts(out, S("\n    "));
             p = p->next;
         }
         
         writer_puts(out, S("   </D:prop>\n"
-                           "   <D:status>HTTP/1.1 200 OK</D:status>"
+                           "   <D:status>HTTP/1.1 200 OK</D:status>\n"
                            "  </D:propstat>\n"));
     }
     
     // send error properties
     PropertyErrorList *error = rp->errors;
     while(error) {
+        writer_puts(out, S("  <D:propstat>\n"
+                           "   <D:prop>\n"));
+        
         WebdavPList *errprop = error->begin;
         while(errprop) {
+            writer_puts(out, S("    "));
             if(send_property(ms, errprop->property, NULL, FALSE, out)) {
                 return out->error;
             }
+            writer_puts(out, S("\n    "));
             errprop = errprop->next;
         }
+        
+        char statuscode[8];
+        int sclen = snprintf(statuscode, 8, "%d ", error->status);
+        if(sclen > 4) {
+            statuscode[0] = '5';
+            statuscode[1] = '0';
+            statuscode[2] = '0';
+            statuscode[3] = ' ';
+            sclen = 4;
+        }
+        writer_puts(out, S("   </D:prop>\n"
+                           "   <D:status>HTTP/1.1 "));
+        writer_put(out, statuscode, sclen);
+        const char *status_msg = protocol_status_message(error->status);
+        if(status_msg) {
+            writer_put(out, status_msg, strlen(status_msg));
+        } else {
+            writer_puts(out, S("Server Error"));
+        }
+        writer_puts(out, S("</D:status>\n"
+                           "  </D:propstat>\n"));
+        
+        
         error = error->next;
     }
     
+    // end response tag
+    writer_puts(out, S(" </D:response>\n"));
+    
     return out->error;
 }
 
 int multistatus_send(Multistatus *ms, SYS_NETFD net) {
+    // start http response
+    protocol_status(ms->sn, ms->rq, 207, NULL);
+    protocol_start_response(ms->sn, ms->rq);
+    
     char buffer[MULTISTATUS_BUFFER_LENGTH];
     // create a writer, that flushes the buffer when it is filled
     Writer writer;
@@ -235,6 +277,11 @@
         response = response->next;
     }
     
+    // end multistatus
+    writer_puts(out, S("</D:multistatus>\n"));
+    
+    writer_flush(out);
+    
     return 0;
 }
 
--- a/src/server/webdav/webdav.c	Sat Jan 18 13:48:59 2020 +0100
+++ b/src/server/webdav/webdav.c	Sat Jan 18 16:31:52 2020 +0100
@@ -54,7 +54,9 @@
 
 static WebdavProperty dav_resourcetype_empty;
 static WebdavProperty dav_resourcetype_collection;
-static WSXmlNode dav_resourcetype_collection_value; // TODO: change type to WSXmlData
+static WSXmlData dav_resourcetype_collection_value;
+
+#define WEBDAV_RESOURCE_TYPE_COLLECTION "<D:collection/>"
 
 static void init_default_backend(void) {
     memset(&default_backend, 0, sizeof(WebdavBackend));
@@ -101,11 +103,10 @@
     
     dav_resourcetype_collection.namespace = &dav_namespace;
     dav_resourcetype_collection.name = "resourcetype";
-    dav_resourcetype_collection.value.node = &dav_resourcetype_collection_value;
-    dav_resourcetype_collection.vtype = WS_VALUE_XML_NODE;
-    dav_resourcetype_collection_value.content = (xmlChar*)"<D:collection/>";
-    dav_resourcetype_collection_value.type = XML_TEXT_NODE;
-    
+    dav_resourcetype_collection.value.data = &dav_resourcetype_collection_value;
+    dav_resourcetype_collection.vtype = WS_VALUE_XML_DATA;
+    dav_resourcetype_collection_value.data = WEBDAV_RESOURCE_TYPE_COLLECTION;
+    dav_resourcetype_collection_value.length = sizeof(WEBDAV_RESOURCE_TYPE_COLLECTION)-1;
     
     return REQ_PROCEED;
 }
@@ -262,20 +263,20 @@
         }
     }
     
-    // finish the propfind request
-    // this function should cleanup all resources, therefore we execute it
-    // even if a previous function failed
-    if(webdav_op_propfind_finish(op)) {
-        ret = REQ_ABORTED;
-    }
-    
     // if propfind was successful, send the result to the client
     if(ret == REQ_PROCEED && multistatus_send(ms, sn->csd)) {
         ret = REQ_ABORTED;
         // TODO: log error
     } else {
+        // TODO: error response
+    }
+    
+    // finish the propfind request
+    // this function should cleanup all resources, therefore we execute it
+    // even if a previous function failed
+    if(webdav_op_propfind_finish(op)) {
         // TODO: log error
-        // TODO: error response
+        ret = REQ_ABORTED;
     }
     
     return ret;
--- a/src/server/webdav/xml.c	Sat Jan 18 13:48:59 2020 +0100
+++ b/src/server/webdav/xml.c	Sat Jan 18 16:31:52 2020 +0100
@@ -344,19 +344,30 @@
 /*
  * Serialize an XML text node
  * This replaces some special characters with entity refs
+ * type: 0 = element text, 1 = attribute text
  */
-static void xml_ser_text(Writer *out, const char *text) {
+static void xml_ser_text(Writer *out, int type, const char *text) {
     size_t start = 0;
     size_t i;
     sstr_t entityref = { NULL, 0 };
     for(i=0;text[i]!='\0';i++) {
-        switch(text[i]) {
-            case '<': entityref = S("&lt;"); break;
-            case '>': entityref = S("&gt;"); break;
-            case '&': entityref = S("&amp;"); break;
-            case '\"': entityref = S("&quot;"); break;
-            case '\'': entityref = S("&apos;"); break;
+        char c = text[i];
+        if(c == '&') {
+            entityref = S("&amp;");
+        } else if(type == 0) {
+            if(c == '<') {
+                entityref = S("&lt;");
+            } else if(c == '>') {
+                entityref = S("&gt;");
+            }
+        } else {
+            if(c == '\"') {
+                entityref = S("&quot;");
+            } else if(c == '\'') {
+                entityref = S("&apos;");
+            }
         }
+        
         if(entityref.ptr) {
             size_t len = i-start;
             if(len > 0) {
@@ -436,7 +447,7 @@
         xmlNode *value = attr->children;
         while(value) {
             if(value->content) {
-                xml_ser_text(out, (const char*)value->content);
+                xml_ser_text(out, 1, (const char*)value->content);
             }
             value = value->next;
         }
@@ -459,7 +470,7 @@
         case XML_ELEMENT_NODE: xml_ser_element(xw, node); break;
         case XML_ATTRIBUTE_NODE: break;
         case XML_TEXT_NODE: {
-            xml_ser_text(xw->out, (const char*)node->content);
+            xml_ser_text(xw->out, 0, (const char*)node->content);
             break;
         }
         case XML_CDATA_SECTION_NODE: {

mercurial