# HG changeset patch # User Olaf Wintermann # Date 1594483080 -7200 # Node ID f727a21497bb3c438dbb751edfa04c8c56f2065b # Parent f4d93355b054d38d627fc761c0e7430b3cbbb92a add basic PUT implementation and tests diff -r f4d93355b054 -r f727a21497bb src/server/daemon/protocol.c --- 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; } diff -r f4d93355b054 -r f727a21497bb src/server/test/main.c --- 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); diff -r f4d93355b054 -r f727a21497bb src/server/test/testutils.c --- 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; } diff -r f4d93355b054 -r f727a21497bb src/server/test/testutils.h --- 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; diff -r f4d93355b054 -r f727a21497bb src/server/test/vfs.c --- 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 #include +#include #include #include @@ -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; } diff -r f4d93355b054 -r f727a21497bb src/server/test/webdav.c --- 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; +} diff -r f4d93355b054 -r f727a21497bb src/server/test/webdav.h --- 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 --------------------------- */ diff -r f4d93355b054 -r f727a21497bb src/server/util/io.c --- 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); } diff -r f4d93355b054 -r f727a21497bb src/server/webdav/multistatus.c --- 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); diff -r f4d93355b054 -r f727a21497bb src/server/webdav/webdav.c --- 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) {