Browse Source

Fix #6. (Improved the upload handling)

silvioprog 7 years ago
parent
commit
bca69ca3e2
5 changed files with 172 additions and 237 deletions
  1. 8 7
      include/sagui.h
  2. 77 141
      src/sg_httpuplds.c
  3. 3 3
      src/sg_httpuplds.h
  4. 1 1
      test/test_httpsrv.c
  5. 83 85
      test/test_httpuplds.c

+ 8 - 7
include/sagui.h

@@ -102,7 +102,7 @@ typedef void (*sg_err_cb)(void *cls, const char *err);
  * \param[out] size Size of the current buffer to be written.
  * \param[out] size Size of the current buffer to be written.
  * \return Total written buffer.
  * \return Total written buffer.
  */
  */
-typedef size_t (*sg_write_cb)(void *handle, uint64_t offset, const char *buf, size_t size);
+typedef ssize_t (*sg_write_cb)(void *handle, uint64_t offset, const char *buf, size_t size);
 
 
 /**
 /**
  * Callback signature used by functions that read streams.
  * Callback signature used by functions that read streams.
@@ -146,7 +146,7 @@ typedef int (*sg_save_as_cb)(void *handle, const char *path, bool overwritten);
 SG_EXTERN unsigned int sg_version(void);
 SG_EXTERN unsigned int sg_version(void);
 
 
 /**
 /**
- * Returns the library version number as string.
+ * Returns the library version number as string in the format N.N.N.
  * \return Library version packed into a null-terminated string.
  * \return Library version packed into a null-terminated string.
  */
  */
 SG_EXTERN const char *sg_version_str(void);
 SG_EXTERN const char *sg_version_str(void);
@@ -204,6 +204,7 @@ SG_EXTERN char *sg_extract_entrypoint(const char *path);
  * Returns the system temporary directory.
  * Returns the system temporary directory.
  * \return Temporary directory as null-terminated string.
  * \return Temporary directory as null-terminated string.
  * \retval NULL If no memory space is available.
  * \retval NULL If no memory space is available.
+ * \warning The caller must free the returned value.
  */
  */
 SG_EXTERN char *sg_tmpdir(void);
 SG_EXTERN char *sg_tmpdir(void);
 
 
@@ -1075,7 +1076,7 @@ SG_EXTERN size_t sg_httpsrv_post_buf_size(struct sg_httpsrv *srv);
 /**
 /**
  * Sets a limit to the total payload.
  * Sets a limit to the total payload.
  * \param[in] srv Server handle.
  * \param[in] srv Server handle.
- * \param[in] limit Payload total limit.
+ * \param[in] limit Payload total limit. Use zero for no limit.
  * \retval 0 - Success.
  * \retval 0 - Success.
  * \retval EINVAL - Invalid argument.
  * \retval EINVAL - Invalid argument.
  */
  */
@@ -1092,7 +1093,7 @@ SG_EXTERN size_t sg_httpsrv_payld_limit(struct sg_httpsrv *srv);
 /**
 /**
  * Sets a limit to the total uploads.
  * Sets a limit to the total uploads.
  * \param[in] srv Server handle.
  * \param[in] srv Server handle.
- * \param[in] limit Uploads total limit.
+ * \param[in] limit Uploads total limit. Use zero for no limit.
  * \retval 0 - Success.
  * \retval 0 - Success.
  * \retval EINVAL - Invalid argument.
  * \retval EINVAL - Invalid argument.
  */
  */
@@ -1109,7 +1110,7 @@ SG_EXTERN uint64_t sg_httpsrv_uplds_limit(struct sg_httpsrv *srv);
 /**
 /**
  * Sets the size for the thread pool.
  * Sets the size for the thread pool.
  * \param[in] srv Server handle.
  * \param[in] srv Server handle.
- * \param[in] size Thread pool size.
+ * \param[in] size Thread pool size. Size greater than 1 enables the thread pooling.
  * \retval 0 - Success.
  * \retval 0 - Success.
  * \retval EINVAL - Invalid argument.
  * \retval EINVAL - Invalid argument.
  */
  */
@@ -1126,7 +1127,7 @@ SG_EXTERN unsigned int sg_httpsrv_thr_pool_size(struct sg_httpsrv *srv);
 /**
 /**
  * Sets the inactivity time to a client get time out.
  * Sets the inactivity time to a client get time out.
  * \param[in] srv Server handle.
  * \param[in] srv Server handle.
- * \param[in] timeout Timeout (in seconds).
+ * \param[in] timeout Timeout (in seconds). Use zero for infinity timeout.
  * \retval 0 - Success.
  * \retval 0 - Success.
  * \retval EINVAL - Invalid argument.
  * \retval EINVAL - Invalid argument.
  */
  */
@@ -1143,7 +1144,7 @@ SG_EXTERN unsigned int sg_httpsrv_con_timeout(struct sg_httpsrv *srv);
 /**
 /**
  * Sets the limit of concurrent connections.
  * Sets the limit of concurrent connections.
  * \param[in] srv Server handle.
  * \param[in] srv Server handle.
- * \param[in] limit Concurrent connections limit.
+ * \param[in] limit Concurrent connections limit. Use zero for no limit.
  * \retval 0 - Success.
  * \retval 0 - Success.
  * \retval EINVAL - Invalid argument.
  * \retval EINVAL - Invalid argument.
  */
  */

+ 77 - 141
src/sg_httpuplds.c

@@ -84,7 +84,7 @@ static int sg__httpuplds_iter(void *cls, __SG_UNUSED enum MHD_ValueKind kind, co
                                          key, filename, content_type, transfer_encoding) != 0)
                                          key, filename, content_type, transfer_encoding) != 0)
                     return MHD_NO;
                     return MHD_NO;
             }
             }
-            if (holder->srv->upld_write_cb(holder->req->curr_upld->handle, off, data, size) == (size_t) -1)
+            if (holder->srv->upld_write_cb(holder->req->curr_upld->handle, off, data, size) == -1)
                 return MHD_NO;
                 return MHD_NO;
             holder->req->curr_upld->size += size;
             holder->req->curr_upld->size += size;
             if (holder->srv->uplds_limit > 0) {
             if (holder->srv->uplds_limit > 0) {
@@ -154,135 +154,77 @@ void sg__httpuplds_cleanup(struct sg_httpsrv *srv, struct sg_httpreq *req) {
 
 
 int sg__httpupld_cb(void *cls, void **handle, const char *dir, __SG_UNUSED const char *field, const char *name,
 int sg__httpupld_cb(void *cls, void **handle, const char *dir, __SG_UNUSED const char *field, const char *name,
                     __SG_UNUSED const char *mime, __SG_UNUSED const char *encoding) {
                     __SG_UNUSED const char *mime, __SG_UNUSED const char *encoding) {
+    char err[SG_ERR_SIZE >> 2];
     struct sg__httpupld *h;
     struct sg__httpupld *h;
     struct stat sbuf;
     struct stat sbuf;
-    char err[SG_ERR_SIZE >> 2];
-    int fd, errnum;
+    int errnum;
     sg__new(h);
     sg__new(h);
-    *handle = h;
+    h->fd = -1;
     h->srv = cls;
     h->srv = cls;
-    if (stat(dir, &sbuf) != 0) {
-        errnum = errno;
-        sg__httpuplds_err(cls, _("Cannot find directory \"%s\": %s.\n"), dir, sg_strerror(errnum, err, sizeof(err)));
-        goto fail;
+    if (stat(dir, &sbuf)) {
+        sg__httpuplds_err(cls, _("Cannot find uploads directory \"%s\": %s.\n"), dir,
+                          sg_strerror(errno, err, sizeof(err)));
+        return ENOENT;
     }
     }
     if (!S_ISDIR(sbuf.st_mode)) {
     if (!S_ISDIR(sbuf.st_mode)) {
-        errnum = ENOTDIR;
-        sg__httpuplds_err(cls, _("Cannot access directory \"%s\": %s.\n"), dir,
-                          sg_strerror(errnum, err, sizeof(err)));
-        goto fail;
-    }
-    if (!(h->path = sg__strjoin(PATH_SEP, dir, "sg_upld_tmp_XXXXXX"))) {
-        errnum = ENOMEM;
-        goto fail;
-    }
-    if (!(h->dest_path = sg__strjoin(PATH_SEP, dir, name))) {
-        errnum = ENOMEM;
-        goto fail;
-    }
-    fd = mkstemp(h->path);
-    if (fd == -1) {
-        errnum = errno;
-        sg__httpuplds_err(cls, _("Cannot create temporary file in \"%s\": %s.\n"), dir,
-                          sg_strerror(errnum, err, sizeof(err)));
-        goto fail;
+        sg__httpuplds_err(cls, _("Cannot access uploads directory \"%s\": %s.\n"), dir,
+                          sg_strerror(ENOTDIR, err, sizeof(err)));
+        return ENOTDIR;
     }
     }
-    h->file = fdopen(fd, "wb");
-    if (!h->file) {
+    if (!(h->path = sg__strjoin(PATH_SEP, dir, "sg_upld_tmp_XXXXXX")) ||
+        !(h->dest = sg__strjoin(PATH_SEP, dir, name)))
+        oom();
+    if ((h->fd = mkstemp(h->path)) == -1) {
         errnum = errno;
         errnum = errno;
-        close(fd);
-        unlink(h->path);
-        sg__httpuplds_err(cls, _("Cannot open temporary file \"%s\": %s.\n"), h->path,
+        sg__httpuplds_err(cls, _("Cannot create temporary upload file in \"%s\": %s.\n"), dir,
                           sg_strerror(errnum, err, sizeof(err)));
                           sg_strerror(errnum, err, sizeof(err)));
-        goto fail;
+        return errnum;
     }
     }
+    *handle = h;
     return 0;
     return 0;
-fail:
-    sg__free(h->path);
-    sg__free(h->dest_path);
-    sg__free(h);
-    *handle = NULL;
-    if (errnum == ENOMEM)
-        oom();
-    return errnum;
 }
 }
 
 
-size_t sg__httpupld_write_cb(void *handle, __SG_UNUSED uint64_t offset, const char *buf, size_t size) {
-    struct sg__httpupld *h = handle;
-    size_t written = fwrite(buf, 1, size, h->file);
-    if (written != size) {
-        fclose(h->file);
-        h->file = NULL;
-        unlink(h->path);
-        sg__httpuplds_err(h->srv, _("Cannot write temporary file \"%s\".\n"), h->path);
-        return (size_t) -1;
-    }
-    return written;
+ssize_t sg__httpupld_write_cb(void *handle, __SG_UNUSED uint64_t offset, const char *buf, size_t size) {
+    return write(((struct sg__httpupld *) handle)->fd, buf, size);
 }
 }
 
 
 void sg__httpupld_free_cb(void *handle) {
 void sg__httpupld_free_cb(void *handle) {
     struct sg__httpupld *h;
     struct sg__httpupld *h;
-    char err[SG_ERR_SIZE >> 2];
     if (!(h = handle))
     if (!(h = handle))
         return;
         return;
-    if (!h->file)
-        goto done;
-    if (fclose(h->file) == 0) {
-        if (unlink(h->path) != 0) {
-            sg__httpuplds_err(h->srv, _("Cannot remove temporary file \"%s\": %s.\n"), h->path,
-                              sg_strerror(errno, err, sizeof(err)));
-        }
-        goto done;
-    } else
-        sg__httpuplds_err(h->srv, _("Cannot close temporary file \"%s\": %s.\n"), h->path,
-                          sg_strerror(errno, err, sizeof(err)));
-done:
+    if (h->fd != -1)
+        close(h->fd);
+    h->fd = -1;
+    unlink(h->path);
     sg__free(h->path);
     sg__free(h->path);
-    sg__free(h->dest_path);
+    sg__free(h->dest);
     sg__free(h);
     sg__free(h);
 }
 }
 
 
 int sg__httpupld_save_cb(void *handle, bool overwritten) {
 int sg__httpupld_save_cb(void *handle, bool overwritten) {
     struct sg__httpupld *h = handle;
     struct sg__httpupld *h = handle;
-    return h ? sg__httpupld_save_as_cb(h, h->dest_path, overwritten) : EINVAL;
+    return h ? sg__httpupld_save_as_cb(h, h->dest, overwritten) : EINVAL;
 }
 }
 
 
 int sg__httpupld_save_as_cb(void *handle, const char *path, bool overwritten) {
 int sg__httpupld_save_as_cb(void *handle, const char *path, bool overwritten) {
-    struct sg__httpupld *h;
+    struct sg__httpupld *h = handle;
     struct stat sbuf;
     struct stat sbuf;
-    int errnum;
-    if (!handle)
-        return EINVAL;
-    h = handle;
-    if (!h->file)
+    if (!handle || !path || (h->fd < 0))
         return EINVAL;
         return EINVAL;
-    if ((errnum = fclose(h->file)) != 0)
-        return -errnum;
-    h->file = NULL;
-    if (!path) {
-        errnum = EINVAL;
-        goto fail;
-    }
-    if ((stat(path, &sbuf) == 0) && S_ISDIR(sbuf.st_mode)) {
-        errnum = EISDIR;
-        goto fail;
-    }
-    if (access(path, F_OK) == 0) {
+    if (close(h->fd))
+        return errno;
+    h->fd = -1;
+    if ((stat(path, &sbuf) >= 0) && S_ISDIR(sbuf.st_mode))
+        return EISDIR;
+    if (!access(path, F_OK)) {
         if (overwritten)
         if (overwritten)
             unlink(path);
             unlink(path);
-        else {
-            errnum = EEXIST;
-            goto fail;
-        }
-    }
-    if (sg__rename(h->path, path) != 0) {
-        errnum = errno;
-        goto fail;
+        else
+            return EEXIST;
     }
     }
+    if (sg__rename(h->path, path))
+        return errno;
     return 0;
     return 0;
-fail:
-    unlink(h->path);
-    return errnum;
 }
 }
 
 
 int sg_httpuplds_iter(struct sg_httpupld *uplds, sg_httpuplds_iter_cb cb, void *cls) {
 int sg_httpuplds_iter(struct sg_httpupld *uplds, sg_httpuplds_iter_cb cb, void *cls) {
@@ -299,10 +241,11 @@ int sg_httpuplds_iter(struct sg_httpupld *uplds, sg_httpuplds_iter_cb cb, void *
 }
 }
 
 
 int sg_httpuplds_next(struct sg_httpupld **upld) {
 int sg_httpuplds_next(struct sg_httpupld **upld) {
-    if (!upld)
-        return EINVAL;
-    *upld = (*upld) ? (*upld)->next : NULL;
-    return 0;
+    if (upld) {
+        *upld = (*upld) ? (*upld)->next : NULL;
+        return 0;
+    }
+    return EINVAL;
 }
 }
 
 
 unsigned int sg_httpuplds_count(struct sg_httpupld *uplds) {
 unsigned int sg_httpuplds_count(struct sg_httpupld *uplds) {
@@ -314,69 +257,62 @@ unsigned int sg_httpuplds_count(struct sg_httpupld *uplds) {
 }
 }
 
 
 void *sg_httpupld_handle(struct sg_httpupld *upld) {
 void *sg_httpupld_handle(struct sg_httpupld *upld) {
-    if (!upld) {
-        errno = EINVAL;
-        return NULL;
-    }
-    return upld->handle;
+    if (upld)
+        return upld->handle;
+    errno = EINVAL;
+    return NULL;
 }
 }
 
 
 const char *sg_httpupld_dir(struct sg_httpupld *upld) {
 const char *sg_httpupld_dir(struct sg_httpupld *upld) {
-    if (!upld) {
-        errno = EINVAL;
-        return NULL;
-    }
-    return upld->dir;
+    if (upld)
+        return upld->dir;
+    errno = EINVAL;
+    return NULL;
 }
 }
 
 
 const char *sg_httpupld_field(struct sg_httpupld *upld) {
 const char *sg_httpupld_field(struct sg_httpupld *upld) {
-    if (!upld) {
-        errno = EINVAL;
-        return NULL;
-    }
-    return upld->field;
+    if (upld)
+        return upld->field;
+    errno = EINVAL;
+    return NULL;
 }
 }
 
 
 const char *sg_httpupld_name(struct sg_httpupld *upld) {
 const char *sg_httpupld_name(struct sg_httpupld *upld) {
-    if (!upld) {
-        errno = EINVAL;
-        return NULL;
-    }
-    return upld->name;
+    if (upld)
+        return upld->name;
+    errno = EINVAL;
+    return NULL;
 }
 }
 
 
 const char *sg_httpupld_mime(struct sg_httpupld *upld) {
 const char *sg_httpupld_mime(struct sg_httpupld *upld) {
-    if (!upld) {
-        errno = EINVAL;
-        return NULL;
-    }
-    return upld->mime;
+    if (upld)
+        return upld->mime;
+    errno = EINVAL;
+    return NULL;
 }
 }
 
 
 const char *sg_httpupld_encoding(struct sg_httpupld *upld) {
 const char *sg_httpupld_encoding(struct sg_httpupld *upld) {
-    if (!upld) {
-        errno = EINVAL;
-        return NULL;
-    }
-    return upld->encoding;
+    if (upld)
+        return upld->encoding;
+    errno = EINVAL;
+    return NULL;
 }
 }
 
 
 uint64_t sg_httpupld_size(struct sg_httpupld *upld) {
 uint64_t sg_httpupld_size(struct sg_httpupld *upld) {
-    if (!upld) {
-        errno = EINVAL;
-        return 0;
-    }
-    return upld->size;
+    if (upld)
+        return upld->size;
+    errno = EINVAL;
+    return 0;
 }
 }
 
 
 int sg_httpupld_save(struct sg_httpupld *upld, bool overwritten) {
 int sg_httpupld_save(struct sg_httpupld *upld, bool overwritten) {
-    if (!upld)
-        return EINVAL;
-    return upld->save_cb(upld->handle, overwritten);
+    if (upld)
+        return upld->save_cb(upld->handle, overwritten);
+    return EINVAL;
 }
 }
 
 
 int sg_httpupld_save_as(struct sg_httpupld *upld, const char *path, bool overwritten) {
 int sg_httpupld_save_as(struct sg_httpupld *upld, const char *path, bool overwritten) {
-    if (!upld || !path)
-        return EINVAL;
-    return upld->save_as_cb(upld->handle, path, overwritten);
+    if (upld && path)
+        return upld->save_as_cb(upld->handle, path, overwritten);
+    return EINVAL;
 }
 }

+ 3 - 3
src/sg_httpuplds.h

@@ -50,9 +50,9 @@ struct sg_httpupld {
 
 
 struct sg__httpupld {
 struct sg__httpupld {
     struct sg_httpsrv *srv;
     struct sg_httpsrv *srv;
-    FILE *file;
+    int fd;
     char *path;
     char *path;
-    char *dest_path;
+    char *dest;
 };
 };
 
 
 struct sg__httpupld_holder {
 struct sg__httpupld_holder {
@@ -68,7 +68,7 @@ SG__EXTERN void sg__httpuplds_cleanup(struct sg_httpsrv *srv, struct sg_httpreq
 SG__EXTERN int sg__httpupld_cb(void *cls, void **handle, const char *dir, const char *field, const char *name,
 SG__EXTERN int sg__httpupld_cb(void *cls, void **handle, const char *dir, const char *field, const char *name,
                                const char *mime, const char *encoding);
                                const char *mime, const char *encoding);
 
 
-SG__EXTERN size_t sg__httpupld_write_cb(void *handle, uint64_t offset, const char *buf, size_t size);
+SG__EXTERN ssize_t sg__httpupld_write_cb(void *handle, uint64_t offset, const char *buf, size_t size);
 
 
 SG__EXTERN void sg__httpupld_free_cb(void *handle);
 SG__EXTERN void sg__httpupld_free_cb(void *handle);
 
 

+ 1 - 1
test/test_httpsrv.c

@@ -158,7 +158,7 @@ static int dummy_httpupld_cb(void *cls, void **handle, const char *dir, const ch
     return 0;
     return 0;
 }
 }
 
 
-static size_t dummy_httpupld_write_cb(void *handle, uint64_t offset, const char *buf, size_t size) {
+static ssize_t dummy_httpupld_write_cb(void *handle, uint64_t offset, const char *buf, size_t size) {
     (void) handle;
     (void) handle;
     (void) offset;
     (void) offset;
     (void) buf;
     (void) buf;

+ 83 - 85
test/test_httpuplds.c

@@ -30,6 +30,7 @@
 #include "sg_assert.h"
 #include "sg_assert.h"
 
 
 #include <string.h>
 #include <string.h>
+#include <fcntl.h>
 #include <sagui.h>
 #include <sagui.h>
 #include "sg_utils.h"
 #include "sg_utils.h"
 #include "sg_httpuplds.c"
 #include "sg_httpuplds.c"
@@ -85,12 +86,12 @@ static int empty_httpupld_cb(void *cls, void **handle, const char *dir, const ch
     return -1;
     return -1;
 }
 }
 
 
-static size_t empty_httpupld_write_cb(void *handle, uint64_t offset, const char *buf, size_t size) {
+static ssize_t empty_httpupld_write_cb(void *handle, uint64_t offset, const char *buf, size_t size) {
     (void) handle;
     (void) handle;
     (void) offset;
     (void) offset;
     (void) buf;
     (void) buf;
     (void) size;
     (void) size;
-    return (size_t) -1;
+    return -1;
 }
 }
 
 
 static void test__httpuplds_add(void) {
 static void test__httpuplds_add(void) {
@@ -246,25 +247,25 @@ static void test__httpuplds_cleanup(void) {
 
 
 static void test__httpupld_cb(void) {
 static void test__httpupld_cb(void) {
     const char *dummy_path = TEST_HTTPUPLDS_BASE_PATH "foo.txt", *filename = "foo.txt";
     const char *dummy_path = TEST_HTTPUPLDS_BASE_PATH "foo.txt", *filename = "foo.txt";
-    const size_t len = 3;
+    const ssize_t len = 3;
     char err[256], str[256];
     char err[256], str[256];
-    void *handle;
+    void *handle = NULL;
     struct sg__httpupld *h;
     struct sg__httpupld *h;
-    FILE *file;
     struct sg_httpsrv *srv;
     struct sg_httpsrv *srv;
     char *dir, *dest_path;
     char *dir, *dest_path;
+    int fd;
     memset(err, 0, sizeof(err));
     memset(err, 0, sizeof(err));
     srv = sg_httpsrv_new2(NULL, dummy_httpreq_cb, dummy_err_cb, err);
     srv = sg_httpsrv_new2(NULL, dummy_httpreq_cb, dummy_err_cb, err);
 
 
     ASSERT(sg__httpupld_cb(srv, &handle, "", "", "", "", "") == ENOENT);
     ASSERT(sg__httpupld_cb(srv, &handle, "", "", "", "", "") == ENOENT);
     ASSERT(!handle);
     ASSERT(!handle);
-    snprintf(str, sizeof(str), _("Cannot find directory \"%s\": %s.\n"), "", strerror(ENOENT));
+    snprintf(str, sizeof(str), _("Cannot find uploads directory \"%s\": %s.\n"), "", strerror(ENOENT));
     ASSERT(strcmp(err, str) == 0);
     ASSERT(strcmp(err, str) == 0);
 
 
     unlink(dummy_path);
     unlink(dummy_path);
-    file = fopen(dummy_path, "w");
-    ASSERT(file);
-    ASSERT(fclose(file) == 0);
+    fd = open(dummy_path, O_RDWR | O_CREAT | O_TRUNC, 438);
+    ASSERT(fd > -1);
+    ASSERT(close(fd) == 0);
     ASSERT(access(dummy_path, F_OK) == 0);
     ASSERT(access(dummy_path, F_OK) == 0);
 
 
     memset(err, 0, sizeof(err));
     memset(err, 0, sizeof(err));
@@ -272,14 +273,14 @@ static void test__httpupld_cb(void) {
     unlink(dummy_path);
     unlink(dummy_path);
     ASSERT(!handle);
     ASSERT(!handle);
     memset(str, 0, sizeof(str));
     memset(str, 0, sizeof(str));
-    snprintf(str, sizeof(str), _("Cannot access directory \"%s\": %s.\n"), dummy_path, strerror(ENOTDIR));
+    snprintf(str, sizeof(str), _("Cannot access uploads directory \"%s\": %s.\n"), dummy_path, strerror(ENOTDIR));
     ASSERT(strcmp(err, str) == 0);
     ASSERT(strcmp(err, str) == 0);
 #if defined(__linux__) && !defined(__ANDROID__)
 #if defined(__linux__) && !defined(__ANDROID__)
     memset(err, 0, sizeof(err));
     memset(err, 0, sizeof(err));
     ASSERT(sg__httpupld_cb(srv, &handle, "/", "", "", "", "") == EACCES);
     ASSERT(sg__httpupld_cb(srv, &handle, "/", "", "", "", "") == EACCES);
     ASSERT(!handle);
     ASSERT(!handle);
     memset(str, 0, sizeof(str));
     memset(str, 0, sizeof(str));
-    snprintf(str, sizeof(str), _("Cannot create temporary file in \"%s\": %s.\n"), "/", strerror(EACCES));
+    snprintf(str, sizeof(str), _("Cannot create temporary upload file in \"%s\": %s.\n"), "/", strerror(EACCES));
     ASSERT(strcmp(err, str) == 0);
     ASSERT(strcmp(err, str) == 0);
 #endif
 #endif
 
 
@@ -296,19 +297,19 @@ static void test__httpupld_cb(void) {
     ASSERT(sg__httpupld_save_cb(handle, true) == 0);
     ASSERT(sg__httpupld_save_cb(handle, true) == 0);
     sg__httpupld_free_cb(handle);
     sg__httpupld_free_cb(handle);
     ASSERT(access(dest_path, F_OK) == 0);
     ASSERT(access(dest_path, F_OK) == 0);
-    file = fopen(dest_path, "r");
+    fd = open(dest_path, O_RDONLY);
     sg_free(dest_path);
     sg_free(dest_path);
-    ASSERT(file);
+    ASSERT(fd > -1);
     memset(str, 0, sizeof(str));
     memset(str, 0, sizeof(str));
-    ASSERT(fread(str, 1, len, file) == len);
-    ASSERT(fclose(file) == 0);
+    ASSERT(read(fd, str, len) == len);
+    ASSERT(close(fd) == 0);
     ASSERT(strcmp(str, "foo") == 0);
     ASSERT(strcmp(str, "foo") == 0);
 
 
     sg_httpsrv_free(srv);
     sg_httpsrv_free(srv);
 }
 }
 
 
 static void test__httpupld_write_cb(void) {
 static void test__httpupld_write_cb(void) {
-    const size_t len = 3;
+    const ssize_t len = 3;
     char str[4];
     char str[4];
     struct sg__httpupld *handle = sg_alloc(sizeof(struct sg__httpupld));
     struct sg__httpupld *handle = sg_alloc(sizeof(struct sg__httpupld));
     handle->path = TEST_HTTPUPLDS_BASE_PATH "foo.txt";
     handle->path = TEST_HTTPUPLDS_BASE_PATH "foo.txt";
@@ -316,16 +317,16 @@ static void test__httpupld_write_cb(void) {
     unlink(handle->path);
     unlink(handle->path);
 
 
     ASSERT(access(handle->path, F_OK) == -1);
     ASSERT(access(handle->path, F_OK) == -1);
-    handle->file = fopen(handle->path, "w");
-    ASSERT(handle->file);
+    handle->fd = open(handle->path, O_RDWR | O_CREAT | O_TRUNC, 438);
+    ASSERT(handle->fd > -1);
     ASSERT(sg__httpupld_write_cb(handle, 0, "foo", len - 1) == (len - 1));
     ASSERT(sg__httpupld_write_cb(handle, 0, "foo", len - 1) == (len - 1));
-    ASSERT(fclose(handle->file) == 0);
+    ASSERT(close(handle->fd) == 0);
     ASSERT(access(handle->path, F_OK) == 0);
     ASSERT(access(handle->path, F_OK) == 0);
-    handle->file = fopen(handle->path, "r");
-    ASSERT(handle->file);
+    handle->fd = open(handle->path, O_RDONLY);
+    ASSERT(handle->fd);
     memset(str, 0, sizeof(str));
     memset(str, 0, sizeof(str));
-    ASSERT(fread(str, 1, len, handle->file) == (len - 1));
-    ASSERT(fclose(handle->file) == 0);
+    ASSERT(read(handle->fd, str, len) == (len - 1));
+    ASSERT(close(handle->fd) == 0);
     ASSERT(strcmp(str, "fo") == 0);
     ASSERT(strcmp(str, "fo") == 0);
 
 
     sg_free(handle);
     sg_free(handle);
@@ -333,143 +334,140 @@ static void test__httpupld_write_cb(void) {
 
 
 static void test__httpupld_free_cb(void) {
 static void test__httpupld_free_cb(void) {
     const char *path = TEST_HTTPUPLDS_BASE_PATH "foo.txt";
     const char *path = TEST_HTTPUPLDS_BASE_PATH "foo.txt";
-    char err[256], str[256];
+    char err[256];
     struct sg_httpsrv *saved_srv;
     struct sg_httpsrv *saved_srv;
     struct sg__httpupld *handle = sg_alloc(sizeof(struct sg__httpupld));
     struct sg__httpupld *handle = sg_alloc(sizeof(struct sg__httpupld));
     memset(err, 0, sizeof(err));
     memset(err, 0, sizeof(err));
     handle->srv = sg_httpsrv_new2(NULL, dummy_httpreq_cb, dummy_err_cb, err);
     handle->srv = sg_httpsrv_new2(NULL, dummy_httpreq_cb, dummy_err_cb, err);
     handle->path = sg__strdup(path);
     handle->path = sg__strdup(path);
-    handle->dest_path = NULL;
+    handle->dest = NULL;
 
 
     unlink(handle->path);
     unlink(handle->path);
 
 
-    handle->file = fopen(handle->path, "w");
-    ASSERT(handle->file);
+    handle->fd = open(handle->path, O_RDWR | O_CREAT | O_TRUNC, 438);
+
+    ASSERT(handle->fd > -1);
 #ifdef _WIN32
 #ifdef _WIN32
-    ASSERT(fclose(handle->file) == 0);
+    ASSERT(close(handle->fd) == 0);
 #endif
 #endif
     ASSERT(unlink(handle->path) == 0);
     ASSERT(unlink(handle->path) == 0);
     ASSERT(strcmp(err, "") == 0);
     ASSERT(strcmp(err, "") == 0);
     saved_srv = handle->srv;
     saved_srv = handle->srv;
     sg__httpupld_free_cb(handle);
     sg__httpupld_free_cb(handle);
     sg_httpsrv_free(saved_srv);
     sg_httpsrv_free(saved_srv);
-#ifdef _WIN32
-    snprintf(str, sizeof(str), _("Cannot close temporary file \"%s\": %s.\n"), path, strerror(ENOENT));
-#else
-    snprintf(str, sizeof(str), _("Cannot remove temporary file \"%s\": %s.\n"), path, strerror(ENOENT));
-#endif
-    ASSERT(strcmp(err, str) == 0);
+    ASSERT(handle->fd == -1);
 }
 }
 
 
 static void test__httpupld_save_cb(void) {
 static void test__httpupld_save_cb(void) {
-    const size_t len = 3;
+    const ssize_t len = 3;
     char str[4];
     char str[4];
     struct sg__httpupld *handle = sg_alloc(sizeof(struct sg__httpupld));
     struct sg__httpupld *handle = sg_alloc(sizeof(struct sg__httpupld));
     handle->path = TEST_HTTPUPLDS_BASE_PATH "foo.txt";
     handle->path = TEST_HTTPUPLDS_BASE_PATH "foo.txt";
-    handle->dest_path = TEST_HTTPUPLDS_BASE_PATH "bar.txt";
+    handle->dest = TEST_HTTPUPLDS_BASE_PATH "bar.txt";
 
 
-    unlink(handle->dest_path);
+    unlink(handle->dest);
     unlink(handle->path);
     unlink(handle->path);
 
 
     ASSERT(sg__httpupld_save_cb(NULL, false) == EINVAL);
     ASSERT(sg__httpupld_save_cb(NULL, false) == EINVAL);
 
 
-    handle->file = fopen(handle->path, "w");
-    ASSERT(handle->file);
-    ASSERT(fwrite("foo", 1, len, handle->file) == len);
-    ASSERT(access(handle->dest_path, F_OK) == -1);
+    handle->fd = open(handle->path, O_RDWR | O_CREAT | O_TRUNC, 438);
+    ASSERT(handle->fd > -1);
+    ASSERT(write(handle->fd, "foo", len) == len);
+    ASSERT(access(handle->dest, F_OK) == -1);
     ASSERT(sg__httpupld_save_cb(handle, true) == 0);
     ASSERT(sg__httpupld_save_cb(handle, true) == 0);
-    ASSERT(access(handle->dest_path, F_OK) == 0);
-    handle->file = fopen(handle->dest_path, "r");
-    ASSERT(handle->file);
+    ASSERT(access(handle->dest, F_OK) == 0);
+    handle->fd = open(handle->dest, O_RDONLY);
+    ASSERT(handle->fd > -1);
     memset(str, 0, sizeof(str));
     memset(str, 0, sizeof(str));
-    ASSERT(fread(str, 1, len, handle->file) == len);
-    ASSERT(fclose(handle->file) == 0);
+    ASSERT(read(handle->fd, str, len) == len);
+    ASSERT(close(handle->fd) == 0);
     ASSERT(strcmp(str, "foo") == 0);
     ASSERT(strcmp(str, "foo") == 0);
 
 
     sg_free(handle);
     sg_free(handle);
 }
 }
 
 
 static void test__httpupld_save_as_cb(void) {
 static void test__httpupld_save_as_cb(void) {
-    const size_t len = 3;
+    const ssize_t len = 3;
     const char *bar_path = TEST_HTTPUPLDS_BASE_PATH "bar.txt";
     const char *bar_path = TEST_HTTPUPLDS_BASE_PATH "bar.txt";
     char str[4];
     char str[4];
     char *dir;
     char *dir;
     struct sg__httpupld *handle = sg_alloc(sizeof(struct sg__httpupld));
     struct sg__httpupld *handle = sg_alloc(sizeof(struct sg__httpupld));
     handle->path = TEST_HTTPUPLDS_BASE_PATH "foo.txt";
     handle->path = TEST_HTTPUPLDS_BASE_PATH "foo.txt";
+    handle->fd = -1;
 
 
     unlink(bar_path);
     unlink(bar_path);
     unlink(handle->path);
     unlink(handle->path);
 
 
     ASSERT(sg__httpupld_save_as_cb(NULL, "foo", false) == EINVAL);
     ASSERT(sg__httpupld_save_as_cb(NULL, "foo", false) == EINVAL);
     ASSERT(sg__httpupld_save_as_cb(handle, "foo", false) == EINVAL);
     ASSERT(sg__httpupld_save_as_cb(handle, "foo", false) == EINVAL);
-    handle->file = fopen(handle->path, "w");
-    ASSERT(handle->file);
-    ASSERT(fwrite("foo", 1, len, handle->file) == len);
+    handle->fd = open(handle->path, O_RDWR | O_CREAT | O_TRUNC, 438);
+    ASSERT(handle->fd > -1);
+    ASSERT(write(handle->fd, "foo", len) == len);
     ASSERT(sg__httpupld_save_as_cb(handle, NULL, false) == EINVAL);
     ASSERT(sg__httpupld_save_as_cb(handle, NULL, false) == EINVAL);
 
 
-    handle->file = fopen(bar_path, "w");
-    ASSERT(handle->file);
-    ASSERT(fwrite("bar", 1, len, handle->file) == len);
-    ASSERT(fclose(handle->file) == 0);
+    handle->fd = open(bar_path, O_RDWR | O_CREAT | O_TRUNC, 438);
+    ASSERT(handle->fd > -1);
+    ASSERT(write(handle->fd, "bar", len) == len);
+    ASSERT(close(handle->fd) == 0);
     ASSERT(access(bar_path, F_OK) == 0);
     ASSERT(access(bar_path, F_OK) == 0);
-    handle->file = fopen(bar_path, "r");
-    ASSERT(handle->file);
+    handle->fd = open(bar_path, O_RDONLY);
+    ASSERT(handle->fd > -1);
     memset(str, 0, sizeof(str));
     memset(str, 0, sizeof(str));
-    ASSERT(fread(str, 1, len, handle->file) == len);
-    ASSERT(fclose(handle->file) == 0);
+    ASSERT(read(handle->fd, str, len) == len);
+    ASSERT(close(handle->fd) == 0);
     ASSERT(strcmp(str, "bar") == 0);
     ASSERT(strcmp(str, "bar") == 0);
 
 
-    handle->file = fopen(handle->path, "w");
-    ASSERT(handle->file);
-    ASSERT(fwrite("foo", 1, len, handle->file) == len);
+    handle->fd = open(handle->path, O_RDWR | O_CREAT | O_TRUNC, 438);
+    ASSERT(handle->fd > -1);
+    ASSERT(write(handle->fd, "foo", len) == len);
 
 
     ASSERT(sg__httpupld_save_as_cb(handle, bar_path, false) == EEXIST);
     ASSERT(sg__httpupld_save_as_cb(handle, bar_path, false) == EEXIST);
     ASSERT(access(bar_path, F_OK) == 0);
     ASSERT(access(bar_path, F_OK) == 0);
-    handle->file = fopen(bar_path, "r");
-    ASSERT(handle->file);
+    handle->fd = open(bar_path, O_RDONLY);
+    ASSERT(handle->fd > -1);
     memset(str, 0, sizeof(str));
     memset(str, 0, sizeof(str));
-    ASSERT(fread(str, 1, len, handle->file) == len);
-    ASSERT(fclose(handle->file) == 0);
+    ASSERT(read(handle->fd, str, len) == len);
+    ASSERT(close(handle->fd) == 0);
     ASSERT(strcmp(str, "bar") == 0);
     ASSERT(strcmp(str, "bar") == 0);
 
 
     dir = sg_tmpdir();
     dir = sg_tmpdir();
     ASSERT(dir);
     ASSERT(dir);
     ASSERT(access(dir, F_OK) == 0);
     ASSERT(access(dir, F_OK) == 0);
-    handle->file = fopen(handle->path, "w");
-    ASSERT(handle->file);
-    ASSERT(fwrite("foo", 1, len, handle->file) == len);
+    handle->fd = open(handle->path, O_RDWR | O_CREAT | O_TRUNC, 438);
+    ASSERT(handle->fd > -1);
+    ASSERT(write(handle->fd, "foo", len) == len);
     ASSERT(sg__httpupld_save_as_cb(handle, dir, false) == EISDIR);
     ASSERT(sg__httpupld_save_as_cb(handle, dir, false) == EISDIR);
     sg_free(dir);
     sg_free(dir);
 
 
-    handle->file = fopen(handle->path, "w");
-    ASSERT(handle->file);
-    ASSERT(fwrite("foo", 1, len, handle->file) == len);
+    handle->fd = open(handle->path, O_RDWR | O_CREAT | O_TRUNC, 438);
+    ASSERT(handle->fd > -1);
+    ASSERT(write(handle->fd, "foo", len) == len);
     ASSERT(sg__httpupld_save_as_cb(handle, "", false) == ENOENT);
     ASSERT(sg__httpupld_save_as_cb(handle, "", false) == ENOENT);
 
 
-    handle->file = fopen(handle->path, "w");
-    ASSERT(handle->file);
-    ASSERT(fwrite("foo", 1, len, handle->file) == len);
+    handle->fd = open(handle->path, O_RDWR | O_CREAT | O_TRUNC, 438);
+    ASSERT(handle->fd > -1);
+    ASSERT(write(handle->fd, "foo", len) == len);
     ASSERT(sg__httpupld_save_as_cb(handle, bar_path, true) == 0);
     ASSERT(sg__httpupld_save_as_cb(handle, bar_path, true) == 0);
     ASSERT(access(bar_path, F_OK) == 0);
     ASSERT(access(bar_path, F_OK) == 0);
-    handle->file = fopen(bar_path, "r");
-    ASSERT(handle->file);
+    handle->fd = open(bar_path, O_RDONLY);
+    ASSERT(handle->fd > -1);
     memset(str, 0, sizeof(str));
     memset(str, 0, sizeof(str));
-    ASSERT(fread(str, 1, len, handle->file) == len);
-    ASSERT(fclose(handle->file) == 0);
+    ASSERT(read(handle->fd, str, len) == len);
+    ASSERT(close(handle->fd) == 0);
     ASSERT(strcmp(str, "foo") == 0);
     ASSERT(strcmp(str, "foo") == 0);
 
 
     unlink(bar_path);
     unlink(bar_path);
     ASSERT(access(bar_path, F_OK) == -1);
     ASSERT(access(bar_path, F_OK) == -1);
-    handle->file = fopen(handle->path, "w");
-    ASSERT(handle->file);
-    ASSERT(fwrite("foo", 1, len, handle->file) == len);
+    handle->fd = open(handle->path, O_RDWR | O_CREAT | O_TRUNC, 438);
+    ASSERT(handle->fd > -1);
+    ASSERT(write(handle->fd, "foo", len) == len);
     ASSERT(sg__httpupld_save_as_cb(handle, bar_path, false) == 0);
     ASSERT(sg__httpupld_save_as_cb(handle, bar_path, false) == 0);
     ASSERT(access(bar_path, F_OK) == 0);
     ASSERT(access(bar_path, F_OK) == 0);
-    handle->file = fopen(bar_path, "r");
-    ASSERT(handle->file);
+    handle->fd = open(bar_path, O_RDONLY);
+    ASSERT(handle->fd > -1);
     memset(str, 0, sizeof(str));
     memset(str, 0, sizeof(str));
-    ASSERT(fread(str, 1, len, handle->file) == len);
-    ASSERT(fclose(handle->file) == 0);
+    ASSERT(read(handle->fd, str, len) == len);
+    ASSERT(close(handle->fd) == 0);
     ASSERT(strcmp(str, "foo") == 0);
     ASSERT(strcmp(str, "foo") == 0);
 
 
     sg_free(handle);
     sg_free(handle);