add basic PUT implementation and tests webdav

Sat, 11 Jul 2020 17:58:00 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 11 Jul 2020 17:58:00 +0200
branch
webdav
changeset 251
f727a21497bb
parent 250
f4d93355b054
child 252
5653a9626cc0

add basic PUT implementation and tests

src/server/daemon/protocol.c file | annotate | diff | comparison | revisions
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/vfs.c 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/util/io.c 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
--- a/src/server/daemon/protocol.c	Sun Jun 07 17:18:59 2020 +0200
+++ b/src/server/daemon/protocol.c	Sat Jul 11 17:58:00 2020 +0200
@@ -347,7 +347,7 @@
         }
         
         // set stream property
-        HttpStream *stream = (HttpStream*)sn->csd;
+        HttpStream *stream = (HttpStream*)sn->csd; // TODO: make this typesafe
         stream->chunked_enc = 1;
         rq->rq_attr.chunked = 1;
     }
--- a/src/server/test/main.c	Sun Jun 07 17:18:59 2020 +0200
+++ b/src/server/test/main.c	Sat Jul 11 17:58:00 2020 +0200
@@ -104,6 +104,7 @@
     // webdav methods
     ucx_test_register(suite, test_webdav_propfind);
     ucx_test_register(suite, test_webdav_proppatch);
+    ucx_test_register(suite, test_webdav_put);
        
     // run tests
     ucx_test_run(suite, stdout);
--- a/src/server/test/testutils.c	Sun Jun 07 17:18:59 2020 +0200
+++ b/src/server/test/testutils.c	Sat Jul 11 17:58:00 2020 +0200
@@ -156,10 +156,10 @@
     }
     stream->buf = ucx_buffer_new(NULL, size, flags);
     
-    stream->io.write = test_io_write;
-    stream->io.writev = test_io_writev;
-    stream->io.close = test_io_close;
-    stream->io.finish = test_io_finish;
+    stream->io.st.write = test_io_write;
+    stream->io.st.writev = test_io_writev;
+    stream->io.st.close = test_io_close;
+    stream->io.st.finish = test_io_finish;
     
     return stream;
 }
--- a/src/server/test/testutils.h	Sun Jun 07 17:18:59 2020 +0200
+++ b/src/server/test/testutils.h	Sat Jul 11 17:58:00 2020 +0200
@@ -40,7 +40,7 @@
 #endif
 
 typedef struct TestIOStream {
-    IOStream io;
+    HttpStream io;
     UcxBuffer *buf;
 } TestIOStream;
     
--- a/src/server/test/vfs.c	Sun Jun 07 17:18:59 2020 +0200
+++ b/src/server/test/vfs.c	Sat Jul 11 17:58:00 2020 +0200
@@ -28,6 +28,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <errno.h>
 
 #include <ucx/string.h>
 #include <ucx/list.h>
@@ -49,6 +50,7 @@
     VFSFile file;
     sstr_t path;
     int isdir;
+    UcxBuffer *content;
 } TestVFSFile;
 
 typedef struct TestVFSDir {
@@ -122,6 +124,50 @@
     
 }
 
+ ssize_t testvfs_read(SYS_FILE fd, void *buf, size_t nbyte) {
+     TestVFSFile *file = (TestVFSFile*)fd;
+     return (ssize_t)ucx_buffer_read(buf, 1, nbyte, file->content);
+ }
+ 
+ ssize_t testvfs_write(SYS_FILE fd, const void *buf, size_t nbyte) {
+     TestVFSFile *file = (TestVFSFile*)fd;
+     return (ssize_t)ucx_buffer_write(buf, 1, nbyte, file->content);
+ }
+ 
+ ssize_t testvfs_pread(SYS_FILE fd, void *buf, size_t nbyte, off_t offset) {
+     TestVFSFile *file = (TestVFSFile*)fd;
+     file->content->pos = (size_t)offset;
+     return testvfs_read(fd, buf, nbyte);
+ }
+ 
+ ssize_t testvfs_pwrite(SYS_FILE fd, const void *buf, size_t nbyte, off_t offset) {
+     TestVFSFile *file = (TestVFSFile*)fd;
+     file->content->pos = (size_t)offset;
+     return testvfs_write(fd, buf, nbyte);
+ }
+ 
+ off_t testvfs_seek(SYS_FILE fd, off_t offset, int whence) {
+     TestVFSFile *file = (TestVFSFile*)fd;
+     ucx_buffer_seek(file->content, offset, whence);
+     return (off_t)file->content->pos;
+ }
+ 
+ void testvfs_close(SYS_FILE fd) {
+     TestVFSFile *file = (TestVFSFile*)fd;
+     file->content->pos = 0;
+ }
+
+VFS_IO test_file_io = {
+    testvfs_read,
+    testvfs_write,
+    testvfs_pread,
+    testvfs_pwrite,
+    testvfs_seek,
+    testvfs_close,
+    NULL, /* aio_read */
+    NULL  /* aio_write */
+};
+
 VFS_DIRIO test_dir_io = {
     testvfs_readdir,
     testvfs_dir_close
@@ -145,10 +191,20 @@
             file = pool_malloc(ctx->sn->pool, sizeof(TestVFSFile));
             ZERO(file, sizeof(TestVFSFile));
             file->path = sstrdup_a(session_get_allocator(ctx->sn), s_path);
+            file->file.io = &test_file_io;
+            
+            file->content = pool_calloc(ctx->sn->pool, 1, sizeof(UcxBuffer));
+            file->content->capacity = 2048;
+            file->content->space = pool_malloc(ctx->sn->pool, file->content->capacity);
+            file->content->flags = 0;
+            file->content->pos = 0;
+            file->content->size = 0;
             
             ucx_map_sstr_put(vfs->files, s_path, file);
+        } else {
+            ctx->vfs_errno = ENOENT;
         }
-    } 
+    }
     
     return (SYS_FILE)file;
 }
@@ -164,6 +220,7 @@
     
     file = ucx_map_sstr_get(vfs->files, s_path);
     if(!file) {
+        ctx->vfs_errno = ENOENT;
         return 1;
     }
     
@@ -190,6 +247,7 @@
     
     file = ucx_map_sstr_get(vfs->files, s_path);
     if(!file) {
+        ctx->vfs_errno = ENOENT;
         return NULL;
     }
     
@@ -258,6 +316,7 @@
     TestVFS *vfs = ctx->vfs->instance;
     TestVFSFile *dir = ucx_map_cstr_get(vfs->files, path);
     if(!dir) {
+        ctx->vfs_errno = ENOENT;
         return 1;
     }
     
--- a/src/server/test/webdav.c	Sun Jun 07 17:18:59 2020 +0200
+++ b/src/server/test/webdav.c	Sat Jul 11 17:58:00 2020 +0200
@@ -1104,7 +1104,7 @@
     pblock_nvinsert("uri", "/", rq->reqpb);
     
     st = testutil_iostream(2048, TRUE);
-    sn->csd = st;
+    sn->csd = (IOStream*)st;
     
     if(request_body) {
         testutil_request_body(sn, rq, request_body, strlen(request_body));
@@ -1724,3 +1724,39 @@
     
     UCX_TEST_END;
 }
+
+UCX_TEST(test_webdav_put) {
+    Session *sn;
+    Request *rq; 
+    TestIOStream *st;
+    pblock *pb;
+    
+    const char *content_const = "Hello World";
+    
+    init_test_webdav_method(&sn, &rq, &st, &pb, "PUT", content_const);
+    rq->vfs = testvfs_create(sn);
+    
+    UCX_TEST_BEGIN;
+    
+    int err;
+    
+    pblock_replace("path", "/file0", rq->vars);
+    err = webdav_put(NULL, sn, rq);
+    
+    UCX_TEST_ASSERT(err == REQ_PROCEED, "put failed");
+    
+    VFSContext *vfs = vfs_request_context(sn, rq);
+    SYS_FILE f0 = vfs_open(vfs, "/file0", 0);
+    UCX_TEST_ASSERT(f0, "cannot open file0");
+    
+    char buf[1024];
+    int r = system_fread(f0, buf, 1024);
+    
+    UCX_TEST_ASSERT(r == strlen(content_const), "wrong file size");
+    UCX_TEST_ASSERT(!memcmp(content_const, buf, r), "wrong file content");
+    
+    testutil_destroy_session(sn);
+    testutil_iostream_destroy(st);
+    
+    UCX_TEST_END;
+}
--- a/src/server/test/webdav.h	Sun Jun 07 17:18:59 2020 +0200
+++ b/src/server/test/webdav.h	Sat Jul 11 17:58:00 2020 +0200
@@ -67,6 +67,7 @@
 UCX_TEST(test_webdav_vfs_op_do);
 
 UCX_TEST(test_webdav_delete);
+UCX_TEST(test_webdav_put);
 
 /* --------------------------- PROPFIND --------------------------- */
 
--- a/src/server/util/io.c	Sun Jun 07 17:18:59 2020 +0200
+++ b/src/server/util/io.c	Sat Jul 11 17:58:00 2020 +0200
@@ -282,6 +282,9 @@
     IOStream *fd = st->fd;
     if(st->chunked_enc) {
         struct iovec *io = calloc(iovcnt + 1, sizeof(struct iovec));
+        if(!io) {
+            return 0;
+        }
         char chunk_len[16];
         io[0].iov_base = chunk_len;
         size_t len = 0;
@@ -291,7 +294,10 @@
         io[0].iov_len = snprintf(chunk_len, 16, "\r\n%zx\r\n", len);
         memcpy(io + 1, iovec, iovcnt * sizeof(struct iovec));
         ssize_t r = fd->writev(fd, io, iovcnt + 1);
-        return r - io[0].iov_len;
+        
+        ssize_t ret = r - io[0].iov_len;
+        free(io);
+        return ret;
     } else {
         return fd->writev(fd, iovec, iovcnt);
     }
--- a/src/server/webdav/multistatus.c	Sun Jun 07 17:18:59 2020 +0200
+++ b/src/server/webdav/multistatus.c	Sat Jul 11 17:58:00 2020 +0200
@@ -255,7 +255,7 @@
     return out->error;
 }
 
-int multistatus_send(Multistatus *ms, SYS_NETFD net) {
+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);
--- a/src/server/webdav/webdav.c	Sun Jun 07 17:18:59 2020 +0200
+++ b/src/server/webdav/webdav.c	Sat Jul 11 17:58:00 2020 +0200
@@ -397,7 +397,7 @@
             default: return REQ_ABORTED;
         }
     }
-    
+     
     WebdavBackend *dav =  rq->davCollection ?
                               rq->davCollection : &default_backend;
     
@@ -600,7 +600,65 @@
 }
 
 int webdav_put(pblock *pb, Session *sn, Request *rq) {
-    return REQ_ABORTED;
+    char *path = pblock_findkeyval(pb_key_path, rq->vars);
+
+    VFSContext *vfs = vfs_request_context(sn, rq);
+    if(!vfs) {
+        protocol_status(sn, rq, PROTOCOL_SERVER_ERROR, NULL);
+        return REQ_ABORTED;
+    }
+    
+    struct stat s;
+    int create_file = 0;
+    if(vfs_stat(vfs, path, &s)) {
+        if(vfs->vfs_errno == ENOENT) {
+            create_file = O_CREAT;
+        } else {
+            protocol_status(sn, rq, util_errno2status(vfs->vfs_errno), NULL);
+            return REQ_ABORTED;
+        }
+    }
+    
+    if(S_ISDIR(s.st_mode)) {
+        // PUT on collections is not allowed
+        protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
+        return REQ_ABORTED;
+    }
+    
+    SYS_FILE fd = vfs_open(vfs, path, O_RDONLY | create_file);
+    if(!fd) {
+        // if it fails, vfs_open sets http status code
+        return REQ_ABORTED;
+    }
+    
+    // TODO: check permissions, lock, ...
+    
+    // all checks done
+    
+    char *expect = pblock_findkeyval(pb_key_expect, rq->headers);
+    if(expect) {
+        if(!strcasecmp(expect, "100-continue")) {
+            if(http_send_continue(sn)) {
+                return REQ_ABORTED;
+            }
+        }
+    }
+    
+    char in[4096];
+    int r;
+    while((r = netbuf_getbytes(sn->inbuf, in, 2048)) > 0) {
+        int w = 0;
+        while(w < r) {
+            w += system_fwrite(fd, in, r);
+        }
+    }
+    
+    system_fclose(fd);
+    
+    int status = create_file ? PROTOCOL_CREATED : PROTOCOL_NO_CONTENT;
+    protocol_status(sn, rq, status, NULL);
+    
+    return REQ_PROCEED;
 }
 
 int webdav_copy(pblock *pb, Session *sn, Request *rq) {

mercurial