fops-zip
authorAndy Green <andy@warmcat.com>
Fri, 3 Mar 2017 04:38:10 +0000 (12:38 +0800)
committerAndy Green <andy@warmcat.com>
Sun, 5 Mar 2017 07:32:55 +0000 (15:32 +0800)
21 files changed:
CMakeLists.txt
README.coding.md
lib/context.c
lib/fops-zip.c [new file with mode: 0644]
lib/junzip.c [deleted file]
lib/lejp-conf.c
lib/libev.c
lib/libwebsockets.c
lib/libwebsockets.h
lib/lws-plat-esp32.c
lib/lws-plat-optee.c
lib/lws-plat-unix.c
lib/lws-plat-win.c
lib/output.c
lib/private-libwebsockets.h
lib/server.c
test-server/candide.zip [new file with mode: 0644]
test-server/test-server-http.c
test-server/test-server-libev.c
test-server/test-server-v2.0.c
test-server/test-server.c

index 6429374..b2d3c03 100644 (file)
@@ -655,7 +655,7 @@ endif()
 
 if (LWS_WITH_ZIP_FOPS)
        list(APPEND SOURCES
-               lib/junzip.c)
+               lib/fops-zip.c)
 endif()
 
 # Add helper files for Windows.
@@ -1174,6 +1174,7 @@ if (NOT LWS_WITHOUT_TESTAPPS)
                set(TEST_SERVER_DATA
                        "${PROJECT_SOURCE_DIR}/test-server/favicon.ico"
                        "${PROJECT_SOURCE_DIR}/test-server/leaf.jpg"
+                       "${PROJECT_SOURCE_DIR}/test-server/candide.zip"
                        "${PROJECT_SOURCE_DIR}/test-server/libwebsockets.org-logo.png"
                        "${PROJECT_SOURCE_DIR}/test-server/lws-common.js"
                        "${PROJECT_SOURCE_DIR}/test-server/test.html")
index 342aa3d..15d4c4a 100644 (file)
@@ -120,6 +120,34 @@ the send pipe on the connection is choked but no ack will ever come, so the
 dead connection will never become writeable.  To cover that, you can use TCP
 keepalives (see later in this document) or pings.
 
+@section gzip Serving from inside a zip file
+
+Lws now supports serving gzipped files from inside a zip container.  Thanks to
+Per Bothner for contributing the code.
+
+This has the advtantage that if the client can accept GZIP encoding, lws can
+simply send the gzip-compressed file from inside the zip file with no further
+processing, saving time and bandwidth.
+
+In the case the client can't understand gzip compression, lws automatically
+decompressed the file and sends it normally.
+
+Clients with limited storage and RAM will find this useful; the memory needed
+for the inflate case is constrained so that only one input buffer at a time
+is ever in memory.
+
+To use this feature, ensure LWS_WITH_ZIP_FOPS is enabled at CMake (it is by
+default).
+
+`libwebsockets-test-server-v2.0` includes a mount using this technology
+already, run that test server and navigate to http://localhost:7681/ziptest/candide.html
+
+This will serve the book Candide in html, together with two jpgs, all from
+inside a .zip file in /usr/[local/]share-libwebsockets-test-server/candide.zip
+
+Usage is otherwise automatic, if you arrange a mount that points to the zipfile,
+eg, "/ziptest" -> "mypath/test.zip", then URLs like `/ziptest/index.html` will be
+servied from `index.html` inside `mypath/test.zip`
 
 @section frags Fragmented messages
 
@@ -347,30 +375,51 @@ and then can use helpers to also leverage these platform-independent
 file handling apis
 
 ```
-       static inline lws_fop_fd_t
+       lws_fop_fd_t
        `lws_plat_file_open`(struct lws_plat_file_ops *fops, const char *filename,
-                          lws_filepos_t *filelen, lws_fop_flags_t *flags)
-       static inline int
+                          lws_fop_flags_t *flags)
+       int
        `lws_plat_file_close`(lws_fop_fd_t fop_fd)
 
-       static inline unsigned long
+       unsigned long
        `lws_plat_file_seek_cur`(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
 
-       static inline int
+       int
        `lws_plat_file_read`(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
                   uint8_t *buf, lws_filepos_t len)
 
-       static inline int
+       int
        `lws_plat_file_write`(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
                   uint8_t *buf, lws_filepos_t len )
 ```
 
+Generic helpers are provided which provide access to generic fops information or
+call through to the above fops
+
+```
+lws_filepos_t
+lws_vfs_tell(lws_fop_fd_t fop_fd);
+
+lws_filepos_t
+lws_vfs_get_length(lws_fop_fd_t fop_fd);
+
+uint32_t
+lws_vfs_get_mod_time(lws_fop_fd_t fop_fd);
+
+lws_fileofs_t
+lws_vfs_file_seek_set(lws_fop_fd_t fop_fd, lws_fileofs_t offset);
+
+lws_fileofs_t
+lws_vfs_file_seek_end(lws_fop_fd_t fop_fd, lws_fileofs_t offset);
+```
+
+
 The user code can also override or subclass the file operations, to either
 wrap or replace them.  An example is shown in test server.
 
 ### Changes from v2.1 and before fops
 
-There are three changes:
+There are several changes:
 
 1) Pre-2.2 fops directly used platform file descriptors.  Current fops returns and accepts a wrapper type lws_fop_fd_t which is a pointer to a malloc'd struct containing information specific to the filesystem implementation.
 
@@ -378,6 +427,19 @@ There are three changes:
 
 3) Everything is wrapped in typedefs.  See lws-plat-unix.c for examples of how to implement.
 
+4) Position in the file, File Length, and a copy of Flags left after open are now generically held in the fop_fd.
+VFS implementation must set and manage this generic information now.  See the implementations in lws-plat-unix.c for
+examples.
+
+5) The file length is no longer set at a pointer provided by the open() fop.  The api `lws_vfs_get_length()` is provided to
+get the file length after open.
+
+6) If your file namespace is virtual, ie, is not reachable by platform fops directly, you must set LWS_FOP_FLAG_VIRTUAL
+on the flags during open.
+
+7) There is an optional `mod_time` uint32_t member in the generic fop_fd.  If you are able to set it during open, you
+should indicate it by setting `LWS_FOP_FLAG_MOD_TIME_VALID` on the flags.
+
 @section ecdh ECDH Support
 
 ECDH Certs are now supported.  Enable the CMake option
index 668d16b..024cc81 100644 (file)
@@ -150,7 +150,8 @@ lws_protocol_init(struct lws_context *context)
 
                for (n = 0; n < vh->count_protocols; n++) {
                        wsi.protocol = &vh->protocols[n];
-
+                       if (!vh->protocols[n].name)
+                               continue;
                        pvo = lws_vhost_protocol_options(vh,
                                                         vh->protocols[n].name);
                        if (pvo) {
@@ -403,7 +404,11 @@ lws_create_vhost(struct lws_context *context,
        }
 #endif
 
-       if (info->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS)
+       if (
+#ifdef LWS_WITH_PLUGINS
+           (context->plugin_list) ||
+#endif
+           info->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS)
                vh->protocols = lwsp;
        else {
                vh->protocols = info->protocols;
@@ -571,6 +576,7 @@ LWS_VISIBLE struct lws_context *
 lws_create_context(struct lws_context_creation_info *info)
 {
        struct lws_context *context = NULL;
+       struct lws_plat_file_ops *prev;
 #ifndef LWS_NO_DAEMONIZE
        int pid_daemon = get_daemonize_pid();
 #endif
@@ -621,20 +627,34 @@ lws_create_context(struct lws_context_creation_info *info)
 
        /* default to just the platform fops implementation */
 
-       context->fops_default[0].open           = _lws_plat_file_open;
-       context->fops_default[0].close          = _lws_plat_file_close;
-       context->fops_default[0].seek_cur       = _lws_plat_file_seek_cur;
-       context->fops_default[0].read           = _lws_plat_file_read;
-       context->fops_default[0].write          = _lws_plat_file_write;
-       context->fops_default[0].path_prefix    = "/";
+       context->fops_platform.open             = _lws_plat_file_open;
+       context->fops_platform.close            = _lws_plat_file_close;
+       context->fops_platform.seek_cur         = _lws_plat_file_seek_cur;
+       context->fops_platform.read             = _lws_plat_file_read;
+       context->fops_platform.write            = _lws_plat_file_write;
+       context->fops_platform.fi[0].sig        = NULL;
+
+       /*
+        *  arrange a linear linked-list of fops starting from context->fops
+        *
+        * platform fops
+        * [ -> fops_zip (copied into context so .next settable) ]
+        * [ -> info->fops ]
+        */
 
-       // context->fops_default[1].open is already NULL from zalloc
+       context->fops = &context->fops_platform;
+       prev = (struct lws_plat_file_ops *)context->fops;
 
-       /* it can be overridden from context creation info */
+#if defined(LWS_WITH_ZIP_FOPS)
+       /* make a soft copy so we can set .next */
+       context->fops_zip = fops_zip;
+       prev->next = &context->fops_zip;
+       prev = (struct lws_plat_file_ops *)prev->next;
+#endif
+
+       /* if user provided fops, tack them on the end of the list */
        if (info->fops)
-               context->fops = info->fops;
-       else
-               context->fops = &context->fops_default[0];
+               prev->next = info->fops;
 
        context->reject_service_keywords = info->reject_service_keywords;
        if (info->external_baggage_free_on_destroy)
diff --git a/lib/fops-zip.c b/lib/fops-zip.c
new file mode 100644 (file)
index 0000000..02f4999
--- /dev/null
@@ -0,0 +1,667 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Original code used in this source file:
+ *
+ * https://github.com/PerBothner/DomTerm.git @912add15f3d0aec
+ *
+ * ./lws-term/io.c
+ * ./lws-term/junzip.c
+ *
+ * Copyright (C) 2017  Per Bothner <per@bothner.com>
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * ( copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ *
+ * lws rewrite:
+ *
+ * Copyright (C) 2017  Andy Green <andy@warmcat.com>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include "private-libwebsockets.h"
+
+#include <zlib.h>
+
+/*
+ * This code works with zip format containers which may have files compressed
+ * with gzip deflate (type 8) or store uncompressed (type 0).
+ *
+ * Linux zip produces such zipfiles by default, eg
+ *
+ *  $ zip ../myzip.zip file1 file2 file3
+ */
+
+#define ZIP_COMPRESSION_METHOD_STORE 0
+#define ZIP_COMPRESSION_METHOD_DEFLATE 8
+
+typedef struct {
+       lws_filepos_t           filename_start;
+       uint32_t                crc32;
+       uint32_t                comp_size;
+       uint32_t                uncomp_size;
+       uint32_t                offset;
+       uint32_t                mod_time;
+       uint16_t                filename_len;
+       uint16_t                extra;
+       uint16_t                method;
+       uint16_t                file_com_len;
+} lws_fops_zip_hdr_t;
+
+typedef struct {
+       struct lws_fop_fd       fop_fd; /* MUST BE FIRST logical fop_fd into
+                                        * file inside zip: fops_zip fops */
+       lws_fop_fd_t            zip_fop_fd; /* logical fop fd on to zip file
+                                            * itself: using platform fops */
+       lws_fops_zip_hdr_t      hdr;
+       z_stream                inflate;
+       lws_filepos_t           content_start;
+       lws_filepos_t           exp_uncomp_pos;
+       union {
+               uint8_t         trailer8[8];
+               uint32_t        trailer32[2];
+       } u;
+       uint8_t                 rbuf[128]; /* decompression chunk size */
+       int                     entry_count;
+
+       unsigned int            decompress:1; /* 0 = direct from file */
+       unsigned int            add_gzip_container:1;
+} *lws_fops_zip_t;
+
+struct lws_plat_file_ops fops_zip;
+#define fop_fd_to_priv(FD) ((lws_fops_zip_t)(FD))
+
+static const uint8_t hd[] = { 31, 139, 8, 0, 0, 0, 0, 0, 0, 3 };
+
+enum {
+       ZC_SIGNATURE                            = 0,
+       ZC_VERSION_MADE_BY                      = 4,
+       ZC_VERSION_NEEDED_TO_EXTRACT            = 6,
+       ZC_GENERAL_PURPOSE_BIT_FLAG             = 8,
+       ZC_COMPRESSION_METHOD                   = 10,
+       ZC_LAST_MOD_FILE_TIME                   = 12,
+       ZC_LAST_MOD_FILE_DATE                   = 14,
+       ZC_CRC32                                = 16,
+       ZC_COMPRESSED_SIZE                      = 20,
+       ZC_UNCOMPRESSED_SIZE                    = 24,
+       ZC_FILE_NAME_LENGTH                     = 28,
+       ZC_EXTRA_FIELD_LENGTH                   = 30,
+
+       ZC_FILE_COMMENT_LENGTH                  = 32,
+       ZC_DISK_NUMBER_START                    = 34,
+       ZC_INTERNAL_FILE_ATTRIBUTES             = 36,
+       ZC_EXTERNAL_FILE_ATTRIBUTES             = 38,
+       ZC_REL_OFFSET_LOCAL_HEADER              = 42,
+       ZC_DIRECTORY_LENGTH                     = 46,
+
+       ZE_SIGNATURE_OFFSET                     = 0,
+       ZE_DESK_NUMBER                          = 4,
+       ZE_CENTRAL_DIRECTORY_DISK_NUMBER        = 6,
+       ZE_NUM_ENTRIES_THIS_DISK                = 8,
+       ZE_NUM_ENTRIES                          = 10,
+       ZE_CENTRAL_DIRECTORY_SIZE               = 12,
+       ZE_CENTRAL_DIR_OFFSET                   = 16,
+       ZE_ZIP_COMMENT_LENGTH                   = 20,
+       ZE_DIRECTORY_LENGTH                     = 22,
+
+       ZL_REL_OFFSET_CONTENT                   = 28,
+       ZL_HEADER_LENGTH                        = 30,
+
+       LWS_FZ_ERR_SEEK_END_RECORD              = 1,
+       LWS_FZ_ERR_READ_END_RECORD,
+       LWS_FZ_ERR_END_RECORD_MAGIC,
+       LWS_FZ_ERR_END_RECORD_SANITY,
+       LWS_FZ_ERR_CENTRAL_SEEK,
+       LWS_FZ_ERR_CENTRAL_READ,
+       LWS_FZ_ERR_CENTRAL_SANITY,
+       LWS_FZ_ERR_NAME_TOO_LONG,
+       LWS_FZ_ERR_NAME_READ,
+       LWS_FZ_ERR_CONTENT_SANITY,
+       LWS_FZ_ERR_CONTENT_SEEK,
+       LWS_FZ_ERR_SCAN_SEEK,
+       LWS_FZ_ERR_NOT_FOUND,
+       LWS_FZ_ERR_ZLIB_INIT,
+       LWS_FZ_ERR_READ_CONTENT,
+       LWS_FZ_ERR_SEEK_COMPRESSED,
+};
+
+static uint16_t
+get_u16(void *p)
+{
+       const uint8_t *c = (const uint8_t *)p;
+
+       return (uint16_t)((c[0] | (c[1] << 8)));
+}
+
+static uint32_t
+get_u32(void *p)
+{
+       const uint8_t *c = (const uint8_t *)p;
+
+       return (uint32_t)((c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)));
+}
+
+int
+lws_fops_zip_scan(lws_fops_zip_t priv, const char *name, int len)
+{
+       lws_filepos_t amount;
+       uint8_t buf[64];
+       int i;
+
+       if (lws_vfs_file_seek_end(priv->zip_fop_fd, -ZE_DIRECTORY_LENGTH) < 0)
+               return LWS_FZ_ERR_SEEK_END_RECORD;
+
+       if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
+                             ZE_DIRECTORY_LENGTH))
+               return LWS_FZ_ERR_READ_END_RECORD;
+
+       if (amount != ZE_DIRECTORY_LENGTH)
+               return LWS_FZ_ERR_READ_END_RECORD;
+
+       /*
+        * We require the zip to have the last record right at the end
+        * Linux zip always does this if no zip comment.
+        */
+       if (buf[0] != 'P' || buf[1] != 'K' || buf[2] != 5 || buf[3] != 6)
+               return LWS_FZ_ERR_END_RECORD_MAGIC;
+
+       i = get_u16(buf + ZE_NUM_ENTRIES);
+
+       if (get_u16(buf + ZE_DESK_NUMBER) ||
+           get_u16(buf + ZE_CENTRAL_DIRECTORY_DISK_NUMBER) ||
+           i != get_u16(buf + ZE_NUM_ENTRIES_THIS_DISK))
+               return LWS_FZ_ERR_END_RECORD_SANITY;
+
+       /* end record is OK... look for our file in the central dir */
+
+       if (lws_vfs_file_seek_set(priv->zip_fop_fd,
+                                 get_u32(buf + ZE_CENTRAL_DIR_OFFSET)) < 0)
+               return LWS_FZ_ERR_CENTRAL_SEEK;
+
+       while (i--) {
+               priv->content_start = lws_vfs_tell(priv->zip_fop_fd);
+
+               if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
+                                     ZC_DIRECTORY_LENGTH))
+                       return LWS_FZ_ERR_CENTRAL_READ;
+
+               if (amount != ZC_DIRECTORY_LENGTH)
+                       return LWS_FZ_ERR_CENTRAL_READ;
+
+               if (get_u32(buf + ZC_SIGNATURE) != 0x02014B50)
+                       return LWS_FZ_ERR_CENTRAL_SANITY;
+
+               lwsl_debug("cstart 0x%lx\n", priv->content_start);
+
+               priv->hdr.filename_len = get_u16(buf + ZC_FILE_NAME_LENGTH);
+               priv->hdr.extra = get_u16(buf + ZC_EXTRA_FIELD_LENGTH);
+               priv->hdr.filename_start = lws_vfs_tell(priv->zip_fop_fd);
+
+               priv->hdr.method = get_u16(buf + ZC_COMPRESSION_METHOD);
+               priv->hdr.crc32 = get_u32(buf + ZC_CRC32);
+               priv->hdr.comp_size = get_u32(buf + ZC_COMPRESSED_SIZE);
+               priv->hdr.uncomp_size = get_u32(buf + ZC_UNCOMPRESSED_SIZE);
+               priv->hdr.offset = get_u32(buf + ZC_REL_OFFSET_LOCAL_HEADER);
+               priv->hdr.mod_time = get_u32(buf + ZC_LAST_MOD_FILE_TIME);
+               priv->hdr.file_com_len = get_u16(buf + ZC_FILE_COMMENT_LENGTH);
+
+               if (priv->hdr.filename_len != len)
+                       goto next;
+
+               if (len >= sizeof(buf) - 1)
+                       return LWS_FZ_ERR_NAME_TOO_LONG;
+
+               if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
+                                                       &amount, buf, len))
+                       return LWS_FZ_ERR_NAME_READ;
+               if (amount != len)
+                       return LWS_FZ_ERR_NAME_READ;
+
+               buf[len] = '\0';
+               lwsl_debug("check %s vs %s\n", buf, name);
+
+               if (strcmp((const char *)buf, name))
+                       goto next;
+
+               /* we found a match */
+               lws_vfs_file_seek_set(priv->zip_fop_fd, priv->hdr.offset);
+               if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
+                                                       &amount, buf,
+                                                       ZL_HEADER_LENGTH))
+                       return LWS_FZ_ERR_NAME_READ;
+               if (amount != ZL_HEADER_LENGTH)
+                       return LWS_FZ_ERR_NAME_READ;
+
+               priv->content_start = priv->hdr.offset +
+                                     ZL_HEADER_LENGTH +
+                                     priv->hdr.filename_len +
+                                     get_u16(buf + ZL_REL_OFFSET_CONTENT);
+
+               lwsl_debug("content supposed to start at 0x%lx\n",
+                          priv->content_start);
+
+               if (priv->content_start > priv->zip_fop_fd->len)
+                       return LWS_FZ_ERR_CONTENT_SANITY;
+
+               if (lws_vfs_file_seek_set(priv->zip_fop_fd,
+                                         priv->content_start) < 0)
+                       return LWS_FZ_ERR_CONTENT_SEEK;
+
+               /* we are aligned at the start of the content */
+
+               priv->exp_uncomp_pos = 0;
+
+               return 0;
+
+next:
+               if (i && lws_vfs_file_seek_set(priv->zip_fop_fd,
+                                              priv->content_start +
+                                              ZC_DIRECTORY_LENGTH +
+                                              priv->hdr.filename_len +
+                                              priv->hdr.extra +
+                                              priv->hdr.file_com_len) < 0)
+                       return LWS_FZ_ERR_SCAN_SEEK;
+       }
+
+       return LWS_FZ_ERR_NOT_FOUND;
+}
+
+static int
+lws_fops_zip_reset_inflate(lws_fops_zip_t priv)
+{
+       if (priv->decompress)
+               inflateEnd(&priv->inflate);
+
+       priv->inflate.zalloc = Z_NULL;
+       priv->inflate.zfree = Z_NULL;
+       priv->inflate.opaque = Z_NULL;
+       priv->inflate.avail_in = 0;
+       priv->inflate.next_in = Z_NULL;
+
+       if (inflateInit2(&priv->inflate, -MAX_WBITS) != Z_OK) {
+               lwsl_err("inflate init failed\n");
+               return LWS_FZ_ERR_ZLIB_INIT;
+       }
+
+       if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->content_start) < 0)
+               return LWS_FZ_ERR_CONTENT_SEEK;
+
+       priv->exp_uncomp_pos = 0;
+
+       return 0;
+}
+
+static lws_fop_fd_t
+lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
+                 const char *vpath, lws_fop_flags_t *flags)
+{
+       lws_fop_flags_t local_flags = 0;
+       lws_fops_zip_t priv;
+       char rp[192];
+       int m;
+
+       /*
+        * vpath points at the / after the fops signature in vfs_path, eg
+        * with a vfs_path "/var/www/docs/manual.zip/index.html", vpath
+        * will come pointing at "/index.html"
+        */
+
+       priv = lws_zalloc(sizeof(*priv));
+       if (!priv)
+               return NULL;
+
+       priv->fop_fd.fops = &fops_zip;
+
+       m = sizeof(rp) - 1;
+       if ((vpath - vfs_path - 1) < m)
+               m = vpath - vfs_path - 1;
+       strncpy(rp, vfs_path, m);
+       rp[m] = '\0';
+
+       /* open the zip file itself using the incoming fops, not fops_zip */
+
+       priv->zip_fop_fd = fops->LWS_FOP_OPEN(fops, rp, NULL, &local_flags);
+       if (!priv->zip_fop_fd) {
+               lwsl_err("unable to open zip %s\n", rp);
+               goto bail1;
+       }
+
+       if (*vpath == '/')
+               vpath++;
+
+       m = lws_fops_zip_scan(priv, vpath, strlen(vpath));
+       if (m) {
+               lwsl_err("unable to find record matching '%s' %d\n", vpath, m);
+               goto bail2;
+       }
+
+       /* the directory metadata tells us modification time, so pass it on */
+       priv->fop_fd.mod_time = priv->hdr.mod_time;
+       *flags |= LWS_FOP_FLAG_MOD_TIME_VALID | LWS_FOP_FLAG_VIRTUAL;
+       priv->fop_fd.flags = *flags;
+
+       /* The zip fop_fd is left pointing at the start of the content.
+        *
+        * 1) Content could be uncompressed (STORE), and we can always serve
+        *    that directly
+        *
+        * 2) Content could be compressed (GZIP), and the client can handle
+        *    receiving GZIP... we can wrap it in a GZIP header and trailer
+        *    and serve the content part directly.  The flag indicating we
+        *    are providing GZIP directly is set so lws will send the right
+        *    headers.
+        *
+        * 3) Content could be compressed (GZIP) but the client can't handle
+        *    receiving GZIP... we can decompress it and serve as it is
+        *    inflated piecemeal.
+        *
+        * 4) Content may be compressed some unknown way... fail
+        *
+        */
+       if (priv->hdr.method == ZIP_COMPRESSION_METHOD_STORE) {
+               /*
+                * it is stored uncompressed, leave it indicated as
+                * uncompressed, and just serve it from inside the
+                * zip with no gzip container;
+                */
+
+               lwsl_info("direct zip serving (stored)\n");
+
+               priv->fop_fd.len = priv->hdr.uncomp_size;
+
+               return &priv->fop_fd;
+       }
+
+       if ((*flags & LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP) &&
+           priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
+
+               /*
+                * We can serve the gzipped file contents directly as gzip
+                * from inside the zip container; client says it is OK.
+                *
+                * To convert to standalone gzip, we have to add a 10-byte
+                * constant header and a variable 8-byte trailer around the
+                * content.
+                *
+                * The 8-byte trailer is prepared now and held in the priv.
+                */
+
+               lwsl_info("direct zip serving (gzipped)\n");
+
+               priv->fop_fd.len = sizeof(hd) + priv->hdr.comp_size +
+                                  sizeof(priv->u);
+
+               if (lws_is_be()) {
+                       uint8_t *p = priv->u.trailer8;
+
+                       *p++ = (uint8_t)priv->hdr.crc32;
+                       *p++ = (uint8_t)(priv->hdr.crc32 >> 8);
+                       *p++ = (uint8_t)(priv->hdr.crc32 >> 16);
+                       *p++ = (uint8_t)(priv->hdr.crc32 >> 24);
+                       *p++ = (uint8_t)priv->hdr.uncomp_size;
+                       *p++ = (uint8_t)(priv->hdr.uncomp_size >> 8);
+                       *p++ = (uint8_t)(priv->hdr.uncomp_size >> 16);
+                       *p   = (uint8_t)(priv->hdr.uncomp_size >> 24);
+               } else {
+                       priv->u.trailer32[0] = priv->hdr.crc32;
+                       priv->u.trailer32[1] = priv->hdr.uncomp_size;
+               }
+
+               *flags |= LWS_FOP_FLAG_COMPR_IS_GZIP;
+               priv->fop_fd.flags = *flags;
+               priv->add_gzip_container = 1;
+
+               return &priv->fop_fd;
+       }
+
+       if (priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
+
+               /* we must decompress it to serve it */
+
+               lwsl_info("decompressed zip serving\n");
+
+               priv->fop_fd.len = priv->hdr.uncomp_size;
+
+               if (lws_fops_zip_reset_inflate(priv)) {
+                       lwsl_err("inflate init failed\n");
+                       goto bail2;
+               }
+
+               priv->decompress = 1;
+
+               return &priv->fop_fd;
+       }
+
+       /* we can't handle it ... */
+
+       lwsl_err("zipped file %s compressed in unknown way (%d)\n", vfs_path,
+                priv->hdr.method);
+
+bail2:
+       lws_vfs_file_close(&priv->zip_fop_fd);
+bail1:
+       free(priv);
+
+       return NULL;
+}
+
+/* ie, we are closing the fop_fd for the file inside the gzip */
+
+static int
+lws_fops_zip_close(lws_fop_fd_t *fd)
+{
+       lws_fops_zip_t priv = fop_fd_to_priv(*fd);
+
+       if (priv->decompress)
+               inflateEnd(&priv->inflate);
+
+       lws_vfs_file_close(&priv->zip_fop_fd); /* close the gzip fop_fd */
+
+       free(priv);
+       *fd = NULL;
+
+       return 0;
+}
+
+static lws_fileofs_t
+lws_fops_zip_seek_cur(lws_fop_fd_t fd, lws_fileofs_t offset_from_cur_pos)
+{
+       fd->pos += offset_from_cur_pos;
+
+       return fd->pos;
+}
+
+static int
+lws_fops_zip_read(lws_fop_fd_t fd, lws_filepos_t *amount, uint8_t *buf,
+                 lws_filepos_t len)
+{
+       lws_fops_zip_t priv = fop_fd_to_priv(fd);
+       lws_filepos_t ramount, rlen, cur = lws_vfs_tell(fd);
+       int ret;
+
+       if (priv->decompress) {
+
+               if (priv->exp_uncomp_pos != fd->pos) {
+                       /*
+                        *  there has been a seek in the uncompressed fop_fd
+                        * we have to restart the decompression and loop eating
+                        * the decompressed data up to the seek point
+                        */
+                       lwsl_info("seek in decompressed\n");
+
+                       lws_fops_zip_reset_inflate(priv);
+
+                       while (priv->exp_uncomp_pos != fd->pos) {
+                               rlen = len;
+                               if (rlen > fd->pos - priv->exp_uncomp_pos)
+                                       rlen = fd->pos - priv->exp_uncomp_pos;
+                               if (lws_fops_zip_read(fd, amount, buf, rlen))
+                                       return LWS_FZ_ERR_SEEK_COMPRESSED;
+                       }
+                       *amount = 0;
+               }
+
+               priv->inflate.avail_out = len;
+               priv->inflate.next_out = buf;
+
+spin:
+               if (!priv->inflate.avail_in) {
+                       rlen = sizeof(priv->rbuf);
+                       if (rlen > priv->hdr.comp_size -
+                                  (cur - priv->content_start))
+                               rlen = priv->hdr.comp_size -
+                                      (priv->hdr.comp_size -
+                                       priv->content_start);
+
+                       if (priv->zip_fop_fd->fops->LWS_FOP_READ(
+                                       priv->zip_fop_fd, &ramount, priv->rbuf,
+                                       rlen))
+                               return LWS_FZ_ERR_READ_CONTENT;
+
+                       cur += ramount;
+
+                       priv->inflate.avail_in = ramount;
+                       priv->inflate.next_in = priv->rbuf;
+               }
+
+               ret = inflate(&priv->inflate, Z_NO_FLUSH);
+               if (ret == Z_STREAM_ERROR)
+                       return ret;
+
+               switch (ret) {
+               case Z_NEED_DICT:
+                       ret = Z_DATA_ERROR;
+                       /* and fall through */
+               case Z_DATA_ERROR:
+               case Z_MEM_ERROR:
+
+                       return ret;
+               }
+
+               if (!priv->inflate.avail_in && priv->inflate.avail_out &&
+                    cur != priv->content_start + priv->hdr.comp_size)
+                       goto spin;
+
+               *amount = len - priv->inflate.avail_out;
+
+               priv->exp_uncomp_pos += *amount;
+               fd->pos += *amount;
+
+               return 0;
+       }
+
+       if (priv->add_gzip_container) {
+
+               lwsl_info("%s: gzip + container\n", __func__);
+               *amount = 0;
+
+               /* place the canned header at the start */
+
+               if (len && fd->pos < sizeof(hd)) {
+                       rlen = sizeof(hd) - fd->pos;
+                       if (rlen > len)
+                               rlen = len;
+                       /* provide stuff from canned header */
+                       memcpy(buf, hd + fd->pos, rlen);
+                       fd->pos += rlen;
+                       buf += rlen;
+                       len -= rlen;
+                       *amount += rlen;
+               }
+
+               /* serve gzipped data direct from zipfile */
+
+               if (len && fd->pos >= sizeof(hd) &&
+                   fd->pos < priv->hdr.comp_size + sizeof(hd)) {
+
+                       rlen = priv->hdr.comp_size - (priv->zip_fop_fd->pos -
+                                                     priv->content_start);
+                       if (rlen > len)
+                               rlen = len;
+
+                       if (rlen &&
+                           priv->zip_fop_fd->pos < (priv->hdr.comp_size +
+                                                    priv->content_start)) {
+                               if (lws_vfs_file_read(priv->zip_fop_fd,
+                                                     &ramount, buf, rlen))
+                                       return LWS_FZ_ERR_READ_CONTENT;
+                               *amount += ramount;
+                               fd->pos += ramount; // virtual pos
+                               buf += ramount;
+                               len -= ramount;
+                       }
+               }
+
+               /* place the prepared trailer at the end */
+
+               if (len && fd->pos >= priv->hdr.comp_size + sizeof(hd) &&
+                   fd->pos < priv->hdr.comp_size + sizeof(hd) +
+                             sizeof(priv->u)) {
+                       cur = fd->pos - priv->hdr.comp_size - sizeof(hd);
+                       rlen = sizeof(priv->u) - cur;
+                       if (rlen > len)
+                               rlen = len;
+
+                       memcpy(buf, priv->u.trailer8 + cur, rlen);
+
+                       *amount += rlen;
+                       fd->pos += rlen;
+               }
+
+               return 0;
+       }
+
+       lwsl_info("%s: store\n", __func__);
+
+       if (len > priv->hdr.uncomp_size - (cur - priv->content_start))
+               len = priv->hdr.comp_size - (priv->hdr.comp_size -
+                                            priv->content_start);
+
+       if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
+                                                amount, buf, len))
+               return LWS_FZ_ERR_READ_CONTENT;
+
+       return 0;
+}
+
+struct lws_plat_file_ops fops_zip = {
+       lws_fops_zip_open,
+       lws_fops_zip_close,
+       lws_fops_zip_seek_cur,
+       lws_fops_zip_read,
+       NULL,
+       { { ".zip/", 5 }, { ".jar/", 5 }, { ".war/", 5 } },
+       NULL,
+};
diff --git a/lib/junzip.c b/lib/junzip.c
deleted file mode 100644 (file)
index b20d534..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-// Unzip library by Per Bothner and Joonas Pihlajamaa.
-// See junzip.h for license and details.
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <zlib.h>
-
-#include "private-libwebsockets.h"
-
-enum {
-       ZC_SIGNATURE                            = 0,
-       ZC_VERSION_MADE_BY                      = 4,
-       ZC_VERSION_NEEDED_TO_EXTRACT            = 6,
-       ZC_GENERAL_PURPOSE_BIT_FLAG             = 8,
-       ZC_COMPRESSION_METHOD                   = 10,
-       ZC_LAST_MOD_FILE_TIME                   = 12,
-       ZC_LAST_MOD_FILE_DATE                   = 14,
-       ZC_CRC32                                = 16,
-       ZC_COMPRESSED_SIZE                      = 20,
-       ZC_UNCOMPRESSED_SIZE                    = 24,
-       ZC_FILE_NAME_LENGTH                     = 28,
-       ZC_EXTRA_FIELD_LENGTH                   = 30,
-       ZC_FILE_COMMENT_LENGTH                  = 32,
-       ZC_DISK_NUMBER_START                    = 34,
-       ZC_INTERNAL_FILE_ATTRIBUTES             = 36,
-       ZC_EXTERNAL_FILE_ATTRIBUTES             = 38,
-       ZC_RELATIVE_OFFSET_OF_LOCAL_HEADER      = 42,
-       ZC_DIRECTORY_LENGTH                     = 46,
-
-       ZE_SIGNATURE_OFFSET                     = 0,
-       ZE_DESK_NUMBER                          = 4,
-       ZE_CENTRAL_DIRECTORY_DISK_NUMBER        = 6,
-       ZE_NUM_ENTRIES_THIS_DISK                = 8,
-       ZE_NUM_ENTRIES                          = 10,
-       ZE_CENTRAL_DIRECTORY_SIZE               = 12,
-       ZE_CENTRAL_DIRECTORY_OFFSET             = 16,
-       ZE_ZIP_COMMENT_LENGTH                   = 20,
-       ZE_DIRECTORY_LENGTH                     = 22,
-};
-
-static uint16_t
-get_u16(void *p)
-{
-       const uint8_t *c = (const uint8_t *)p;
-
-       return (uint16_t)((c[0] | (c[1] << 8)));
-}
-
-static uint32_t
-get_u32(void *p)
-{
-       const uint8_t *c = (const uint8_t *)p;
-
-       return (uint32_t)((c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)));
-}
-
-static int
-zf_seek_set(jzfile_t *zfile, size_t offset)
-{
-       int new_position = offset;
-
-       if (new_position < 0 || new_position > zfile->length)
-               return -1;
-       zfile->position = new_position;
-
-       return 0;
-}
-
-static int
-zf_seek_cur(jzfile_t *zfile, size_t offset)
-{
-       int new_position = zfile->position + offset;
-
-       if (new_position < 0 || new_position > zfile->length)
-               return -1;
-       zfile->position = new_position;
-
-       return 0;
-}
-
-static int
-zf_seek_end(jzfile_t *zfile, size_t offset)
-{
-       int new_position = zfile->length + offset;
-
-       if (new_position < 0 || new_position > zfile->length)
-               return -1;
-       zfile->position = new_position;
-
-       return 0;
-}
-
-size_t
-zf_read(jzfile_t *zfile, void *buf, size_t size)
-{
-       size_t avail = zfile->length - zfile->position;
-
-       if (size > avail)
-               size = avail;
-       memcpy(buf, zfile->start + zfile->position, size);
-       zfile->position += size;
-
-       return size;
-}
-
-/* Read ZIP file end record. Will move within file. */
-int
-jzReadEndRecord(jzfile_t *zip)
-{
-       unsigned char *ptr = zf_current(zip);
-
-       if (zf_seek_end(zip, -ZE_DIRECTORY_LENGTH))
-               return Z_ERRNO;
-
-       while (ptr[0] != 0x50 || ptr[1] != 0x4B || ptr[2] != 5 || ptr[3] != 6)
-               if (ptr-- == zip->start)
-                       return Z_ERRNO;
-
-       zip->numEntries = get_u16(ptr + ZE_NUM_ENTRIES);
-       zip->centralDirectoryOffset= get_u32(ptr + ZE_CENTRAL_DIRECTORY_OFFSET);
-
-       if (get_u16(ptr + ZE_DESK_NUMBER) ||
-           get_u16(ptr + ZE_CENTRAL_DIRECTORY_DISK_NUMBER) ||
-           zip->numEntries != get_u16(ptr + ZE_NUM_ENTRIES_THIS_DISK))
-               return Z_ERRNO;
-
-       return Z_OK;
-}
-
-/* Read ZIP file global directory. Will move within file. */
-int
-jzReadCentralDirectory(jzfile_t *zip, jzcb_t callback)
-{
-       jzfile_hdr_t h;
-       int i;
-
-       if (zf_seek_set(zip, zip->centralDirectoryOffset))
-               return Z_ERRNO;
-
-       for (i = 0; i < zip->numEntries; i++) {
-               unsigned char *ptr = zf_current(zip);
-
-               if (zf_available(zip) < ZC_DIRECTORY_LENGTH)
-                       return Z_ERRNO;
-
-               zf_seek_cur(zip, ZC_DIRECTORY_LENGTH);
-               if (get_u32(ptr + ZC_SIGNATURE) != 0x02014B50)
-                       return Z_ERRNO;
-
-               // Construct jzfile_hdr_t from global file h
-               h.compressionMethod = get_u16(ptr + ZC_COMPRESSION_METHOD);
-               h.crc32 = get_u32(ptr + ZC_CRC32);
-               h.compressedSize = get_u32(ptr + ZC_COMPRESSED_SIZE);
-               h.uncompressedSize = get_u32(ptr + ZC_UNCOMPRESSED_SIZE);
-               h.fileNameLength = get_u16(ptr + ZC_FILE_NAME_LENGTH);
-               h.extraFieldLength = get_u16(ptr + ZC_EXTRA_FIELD_LENGTH);
-               h.offset = get_u32(ptr + ZC_RELATIVE_OFFSET_OF_LOCAL_HEADER);
-
-               h.fileNameStart = zf_tell(zip);
-               if (zf_seek_cur(zip, h.fileNameLength + h.extraFieldLength +
-                                    get_u16(ptr + ZC_FILE_COMMENT_LENGTH)))
-                       return Z_ERRNO;
-
-               if (!callback(zip, i, &h))
-                       break; // end if callback returns zero
-       }
-
-       return Z_OK;
-}
-
-int jzSeekData(jzfile_t *zip, jzfile_hdr_t *entry)
-{
-       size_t offset = entry->offset;
-
-       offset += ZIP_LOCAL_FILE_HEADER_LENGTH;
-       offset += entry->fileNameLength + entry->extraFieldLength;
-
-       if (offset < 0 || offset > zip->length)
-               return Z_STREAM_END;
-
-       zip->position = offset;
-
-       return Z_OK;
-}
-
-/* Read data from file stream, described by h, to preallocated buffer */
-int
-jzReadData(jzfile_t *zip, jzfile_hdr_t *h, void *buffer)
-{
-       unsigned char *bytes = (unsigned char *)buffer;
-       long compressedLeft, uncompressedLeft;
-       z_stream strm;
-       int ret;
-
-       switch (h->compressionMethod) {
-       case 0: /* Store - just read it */
-               if (zf_read(zip, buffer, h->uncompressedSize) <
-                                        h->uncompressedSize)
-                       return Z_ERRNO;
-               break;
-       case 8: /* Deflate - using zlib */
-               strm.zalloc = Z_NULL;
-               strm.zfree = Z_NULL;
-               strm.opaque = Z_NULL;
-
-               strm.avail_in = 0;
-               strm.next_in = Z_NULL;
-
-               /*
-                * Use inflateInit2 with negative window bits to
-                * indicate raw data
-                */
-               if ((ret = inflateInit2(&strm, -MAX_WBITS)) != Z_OK)
-                       return ret; /* Zlib errors are negative */
-
-               /* Inflate compressed data */
-               for (compressedLeft = h->compressedSize,
-                                     uncompressedLeft = h->uncompressedSize;
-                    compressedLeft && uncompressedLeft && ret != Z_STREAM_END;
-                    compressedLeft -= strm.avail_in) {
-                       /* Read next chunk */
-                       unsigned char *ptr = zf_current(zip);
-
-                       strm.avail_in = compressedLeft;
-                       zf_seek_cur(zip, compressedLeft);
-                       if (strm.avail_in == 0) {
-                               inflateEnd(&strm);
-
-                               return Z_ERRNO;
-                       }
-
-                       strm.next_in = ptr;
-                       strm.avail_out = uncompressedLeft;
-                       strm.next_out = bytes;
-
-                       compressedLeft -= strm.avail_in;
-                       /* inflate will change avail_in */
-
-                       ret = inflate(&strm, Z_NO_FLUSH);
-
-                       if (ret == Z_STREAM_ERROR)
-                               return ret;
-
-                       switch (ret) {
-                       case Z_NEED_DICT:
-                               ret = Z_DATA_ERROR;
-                               /* and fall through */
-                       case Z_DATA_ERROR: case Z_MEM_ERROR:
-                               (void)inflateEnd(&strm);
-
-                               return ret;
-                       }
-
-                       /* bytes uncompressed */
-                       bytes += uncompressedLeft - strm.avail_out;
-                       uncompressedLeft = strm.avail_out;
-               }
-
-               inflateEnd(&strm);
-               break;
-       default:
-               return Z_ERRNO;
-       }
-
-       return Z_OK;
-}
index 6fc358f..8fb0553 100644 (file)
@@ -455,7 +455,8 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
                        "cgi://",
                        ">http://",
                        ">https://",
-                       "callback://"
+                       "callback://",
+                       "gzip://",
                };
 
                if (!a->fresh_mount)
index 3f882b2..71834a2 100644 (file)
@@ -170,7 +170,7 @@ lws_libev_accept(struct lws *new_wsi, lws_sock_file_fd_type desc)
        if (!LWS_LIBEV_ENABLED(context))
                return;
 
-       if (wsi->mode == LWSCM_RAW_FILEDESC)
+       if (new_wsi->mode == LWSCM_RAW_FILEDESC)
                fd = desc.filefd;
        else
                fd = desc.sockfd;
index 43da8fb..3f9612b 100755 (executable)
@@ -220,9 +220,7 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
                        wsi->protocol->callback(wsi,
                                                LWS_CALLBACK_RAW_CLOSE_FILE,
                                                wsi->user_space, NULL, 0);
-                       lws_free_wsi(wsi);
-
-                       return;
+                       goto async_close;
        }
 
 #ifdef LWS_WITH_CGI
@@ -264,8 +262,7 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 
        if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED &&
            wsi->u.http.fop_fd != NULL) {
-               lws_vfs_file_close(wsi->u.http.fop_fd);
-               wsi->u.http.fop_fd = NULL;
+               lws_vfs_file_close(&wsi->u.http.fop_fd);
                wsi->vhost->protocols->callback(wsi,
                        LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0);
                wsi->told_user_closed = 1;
@@ -606,6 +603,7 @@ just_kill_connection:
                       LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0)
                lwsl_warn("ext destroy wsi failed\n");
 
+async_close:
        wsi->socket_is_permanently_unusable = 1;
 
 #ifdef LWS_USE_LIBUV
@@ -971,32 +969,105 @@ lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len)
 LWS_VISIBLE LWS_EXTERN void
 lws_set_fops(struct lws_context *context, struct lws_plat_file_ops *fops)
 {
-       memcpy(&context->fops_default, fops, sizeof *fops);
+       memcpy(&context->fops, fops, sizeof *fops);
+}
+
+LWS_VISIBLE LWS_EXTERN lws_filepos_t
+lws_vfs_tell(lws_fop_fd_t fop_fd)
+{
+       return fop_fd->pos;
+}
+
+LWS_VISIBLE LWS_EXTERN lws_filepos_t
+lws_vfs_get_length(lws_fop_fd_t fop_fd)
+{
+       return fop_fd->len;
+}
+
+LWS_VISIBLE LWS_EXTERN uint32_t
+lws_vfs_get_mod_time(lws_fop_fd_t fop_fd)
+{
+       return fop_fd->mod_time;
+}
+
+LWS_VISIBLE lws_fileofs_t
+lws_vfs_file_seek_set(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
+{
+       lws_fileofs_t ofs;
+       lwsl_debug("%s: seeking to %ld, len %ld\n", __func__, offset, fop_fd->len);
+       ofs = fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, offset - fop_fd->pos);
+       lwsl_debug("%s: result %ld, fop_fd pos %ld\n", __func__, ofs, fop_fd->pos);
+       return ofs;
+}
+
+
+LWS_VISIBLE lws_fileofs_t
+lws_vfs_file_seek_end(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
+{
+       return fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, fop_fd->len + fop_fd->pos + offset);
 }
 
-LWS_VISIBLE LWS_EXTERN const struct lws_plat_file_ops *
-lws_select_fops_by_vfs_path(const struct lws_context *context, const char *vfs_path)
+
+const struct lws_plat_file_ops *
+lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path,
+                   const char **vpath)
 {
-       const struct lws_plat_file_ops *fops = context->fops;
-       int hit = -1, n = 0, matchlen = 0, len;
+       const struct lws_plat_file_ops *pf;
+       const char *p = vfs_path;
+       int n;
+
+       *vpath = NULL;
+
+       /* no non-platform fops, just use that */
+
+       if (!fops->next)
+               return fops;
 
-       while (fops->open) {
-               len = strlen(fops->path_prefix);
+       /*
+        *  scan the vfs path looking for indications we are to be
+        * handled by a specific fops
+        */
+
+       while (*p) {
+               if (*p != '/') {
+                       p++;
+                       continue;
+               }
+               /* the first one is always platform fops, so skip */
+               pf = fops->next;
+               while (pf) {
+                       n = 0;
+                       while (n < ARRAY_SIZE(pf->fi) && pf->fi[n].sig) {
+                               if (p >= vfs_path + pf->fi[n].len)
+                                       if (!strncmp(p - (pf->fi[n].len - 1),
+                                                   pf->fi[n].sig,
+                                                   pf->fi[n].len - 1)) {
+                                               *vpath = p + 1;
+                                               return pf;
+                                       }
 
-               if (!strncmp(vfs_path, fops->path_prefix, len))
-                       if (len > matchlen) {
-                               hit = n;
-                               matchlen = len;
+                               n++;
                        }
-               fops++;
+                       pf = pf->next;
+               }
+               p++;
        }
 
-       if (hit < 0)
-               return context->fops_default;
+       return fops;
+}
+
+LWS_VISIBLE LWS_EXTERN lws_fop_fd_t LWS_WARN_UNUSED_RESULT
+lws_vfs_file_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
+                 lws_fop_flags_t *flags)
+{
+       const char *vpath;
+       const struct lws_plat_file_ops *selected = lws_vfs_select_fops(
+                       fops, vfs_path, &vpath);
 
-       return &context->fops[hit];
+       return selected->LWS_FOP_OPEN(fops, vfs_path, vpath, flags);
 }
 
+
 /**
  * lws_now_secs() - seconds since 1970-1-1
  *
@@ -1396,7 +1467,7 @@ lws_union_transition(struct lws *wsi, enum connection_mode mode)
 LWS_VISIBLE struct lws_plat_file_ops *
 lws_get_fops(struct lws_context *context)
 {
-       return &context->fops_default[0];
+       return (struct lws_plat_file_ops *)context->fops;
 }
 
 LWS_VISIBLE LWS_EXTERN struct lws_context *
index bf7cc64..14682a9 100644 (file)
@@ -33,6 +33,12 @@ extern "C" {
 #include <stdarg.h>
 #endif
 
+static inline int lws_is_be(void) {
+       const int probe = ~0xff;
+
+       return *(const char *)&probe;
+}
+
 #if defined(LWS_WITH_ESP8266)
 struct sockaddr_in;
 #define LWS_POSIX 0
@@ -1816,9 +1822,7 @@ struct lws_context_creation_info {
         * by a sentinel with NULL .open.
         *
         * If NULL, lws provides just the platform file operations struct for
-        * backwards compatibility.  If set to point to an array of fops
-        * structs, lws_select_fops_by_vfs_path() will select the best match
-        * comparing the left of vfs_path to each fops .path_prefix.
+        * backwards compatibility.
         */
 
        /* Add new things just above here ---^
@@ -2559,6 +2563,7 @@ lws_get_mimetype(const char *file, const struct lws_http_mount *m);
 LWS_VISIBLE LWS_EXTERN int
 lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
                    const char *other_headers, int other_headers_len);
+
 LWS_VISIBLE LWS_EXTERN int
 lws_serve_http_file_fragment(struct lws *wsi);
 //@}
@@ -4268,29 +4273,27 @@ lws_cgi_kill(struct lws *wsi);
 
 #if defined(LWS_WITH_ESP32)
 /* sdk preprocessor defs? compiler issue? gets confused with member names */
-#define LWS_FOP_OPEN _open
-#define LWS_FOP_CLOSE _close
-#define LWS_FOP_SEEK_CUR _seek_cur
-#define LWS_FOP_READ _read
-#define LWS_FOP_WRITE _write
+#define LWS_FOP_OPEN           _open
+#define LWS_FOP_CLOSE          _close
+#define LWS_FOP_SEEK_CUR       _seek_cur
+#define LWS_FOP_READ           _read
+#define LWS_FOP_WRITE          _write
 #else
-#define LWS_FOP_OPEN open
-#define LWS_FOP_CLOSE close
-#define LWS_FOP_SEEK_CUR seek_cur
-#define LWS_FOP_READ read
-#define LWS_FOP_WRITE write
+#define LWS_FOP_OPEN           open
+#define LWS_FOP_CLOSE          close
+#define LWS_FOP_SEEK_CUR       seek_cur
+#define LWS_FOP_READ           read
+#define LWS_FOP_WRITE          write
 #endif
 
 #define LWS_FOP_FLAGS_MASK                ((1 << 23) - 1)
 #define LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP (1 << 24)
 #define LWS_FOP_FLAG_COMPR_IS_GZIP        (1 << 25)
+#define LWS_FOP_FLAG_MOD_TIME_VALID       (1 << 26)
+#define LWS_FOP_FLAG_VIRTUAL              (1 << 27)
 
 struct lws_plat_file_ops;
-struct lws_fop_fd {
-       lws_filefd_type fd;
-       const struct lws_plat_file_ops *fops;
-       void *filesystem_priv;
-};
+
 #if defined(WIN32) || defined(_WIN32)
 /* ... */
 #if !defined(ssize_t)
@@ -4313,26 +4316,49 @@ typedef unsigned char uint8_t;
 #endif
 #endif
 
-typedef struct lws_fop_fd *lws_fop_fd_t;
 typedef size_t lws_filepos_t;
 typedef ssize_t lws_fileofs_t;
 typedef uint32_t lws_fop_flags_t;
 
+struct lws_fop_fd {
+       lws_filefd_type                 fd;
+       /**< real file descriptor related to the file... */
+       const struct lws_plat_file_ops  *fops;
+       /**< fops that apply to this fop_fd */
+       void                            *filesystem_priv;
+       /**< ignored by lws; owned by the fops handlers */
+       lws_filepos_t                   pos;
+       /**< generic "position in file" */
+       lws_filepos_t                   len;
+       /**< generic "length of file" */
+       lws_fop_flags_t                 flags;
+       /**< copy of the returned flags */
+       uint32_t                        mod_time;
+       /**< optional "modification time of file", only valid if .open()
+        * set the LWS_FOP_FLAG_MOD_TIME_VALID flag */
+};
+typedef struct lws_fop_fd *lws_fop_fd_t;
+
+struct lws_fops_index {
+       const char *sig;        /* NULL or vfs signature, eg, ".zip/" */
+       uint8_t len;            /* length of above string */
+};
+
 struct lws_plat_file_ops {
        lws_fop_fd_t (*LWS_FOP_OPEN)(const struct lws_plat_file_ops *fops,
-                                    const char *filename,
-                                    lws_filepos_t *filelen,
+                                    const char *filename, const char *vpath,
                                     lws_fop_flags_t *flags);
        /**< Open file (always binary access if plat supports it)
-        * filelen is filled on exit to be the length of the file
+        * vpath may be NULL, or if the fops understands it, the point at which
+        * the filename's virtual part starts.
         * *flags & LWS_FOP_FLAGS_MASK should be set to O_RDONLY or O_RDWR.
         * If the file may be gzip-compressed,
         * LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP is set.  If it actually is
         * gzip-compressed, then the open handler should OR
         * LWS_FOP_FLAG_COMPR_IS_GZIP on to *flags before returning.
         */
-       int (*LWS_FOP_CLOSE)(lws_fop_fd_t fop_fd);
-       /**< close file */
+       int (*LWS_FOP_CLOSE)(lws_fop_fd_t *fop_fd);
+       /**< close file AND set the pointer to NULL */
        lws_fileofs_t (*LWS_FOP_SEEK_CUR)(lws_fop_fd_t fop_fd,
                                          lws_fileofs_t offset_from_cur_pos);
        /**< seek from current position */
@@ -4342,8 +4368,12 @@ struct lws_plat_file_ops {
        int (*LWS_FOP_WRITE)(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
                             uint8_t *buf, lws_filepos_t len);
        /**< Write to file, on exit *amount is set to amount actually written */
-       const char *path_prefix;
-       /**< Optional, NULL or filesystem namespace prefix matching this fops */
+
+       struct lws_fops_index fi[3];
+       /**< vfs path signatures implying use of this fops */
+
+       const struct lws_plat_file_ops *next;
+       /**< NULL or next fops in list */
 
        /* Add new things just above here ---^
         * This is part of the ABI, don't needlessly break compatibility */
@@ -4358,24 +4388,63 @@ LWS_VISIBLE LWS_EXTERN struct lws_plat_file_ops * LWS_WARN_UNUSED_RESULT
 lws_get_fops(struct lws_context *context);
 LWS_VISIBLE LWS_EXTERN void
 lws_set_fops(struct lws_context *context, struct lws_plat_file_ops *fops);
-LWS_VISIBLE LWS_EXTERN const struct lws_plat_file_ops * LWS_WARN_UNUSED_RESULT
-lws_select_fops_by_vfs_path(const struct lws_context *context, const char *vfs_path);
 /**
- * lws_plat_file_open() - file open operations
+ * lws_vfs_tell() - get current file position
+ *
+ * \param fop_fd: fop_fd we are asking about
+ */
+LWS_VISIBLE LWS_EXTERN lws_filepos_t LWS_WARN_UNUSED_RESULT
+lws_vfs_tell(lws_fop_fd_t fop_fd);
+/**
+ * lws_vfs_get_length() - get current file total length in bytes
+ *
+ * \param fop_fd: fop_fd we are asking about
+ */
+LWS_VISIBLE LWS_EXTERN lws_filepos_t LWS_WARN_UNUSED_RESULT
+lws_vfs_get_length(lws_fop_fd_t fop_fd);
+/**
+ * lws_vfs_get_mod_time() - get time file last modified
+ *
+ * \param fop_fd: fop_fd we are asking about
+ */
+LWS_VISIBLE LWS_EXTERN uint32_t LWS_WARN_UNUSED_RESULT
+lws_vfs_get_mod_time(lws_fop_fd_t fop_fd);
+/**
+ * lws_vfs_file_seek_set() - seek relative to start of file
+ *
+ * \param fop_fd: fop_fd we are seeking in
+ * \param offset: offset from start of file
+ */
+LWS_VISIBLE LWS_EXTERN lws_fileofs_t
+lws_vfs_file_seek_set(lws_fop_fd_t fop_fd, lws_fileofs_t offset);
+/**
+ * lws_vfs_file_seek_end() - seek relative to end of file
+ *
+ * \param fop_fd: fop_fd we are seeking in
+ * \param offset: offset from start of file
+ */
+LWS_VISIBLE LWS_EXTERN lws_fileofs_t
+lws_vfs_file_seek_end(lws_fop_fd_t fop_fd, lws_fileofs_t offset);
+
+LWS_VISIBLE LWS_EXTERN struct lws_plat_file_ops fops_zip;
+
+/**
+ * lws_plat_file_open() - open vfs filepath
  *
  * \param fops: file ops struct that applies to this descriptor
- * \param filename: filename to open
- * \param filelen: length of file (filled in by call)
+ * \param vfs_path: filename to open
  * \param flags: pointer to open flags
  *
+ * The vfs_path is scanned for known fops signatures, and the open directed
+ * to any matching fops open.
+ *
+ * User code should use this api to perform vfs opens.
+ *
  * returns semi-opaque handle
  */
-static LWS_INLINE lws_fop_fd_t LWS_WARN_UNUSED_RESULT
-lws_vfs_file_open(const struct lws_plat_file_ops *fops, const char *filename,
-                  lws_filepos_t *filelen, lws_fop_flags_t *flags)
-{
-       return fops->LWS_FOP_OPEN(fops, filename, filelen, flags);
-}
+LWS_VISIBLE LWS_EXTERN lws_fop_fd_t LWS_WARN_UNUSED_RESULT
+lws_vfs_file_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
+                 lws_fop_flags_t *flags);
 
 /**
  * lws_plat_file_close() - close file
@@ -4383,9 +4452,9 @@ lws_vfs_file_open(const struct lws_plat_file_ops *fops, const char *filename,
  * \param fop_fd: file handle to close
  */
 static LWS_INLINE int
-lws_vfs_file_close(lws_fop_fd_t fop_fd)
+lws_vfs_file_close(lws_fop_fd_t *fop_fd)
 {
-       return fop_fd->fops->LWS_FOP_CLOSE(fop_fd);
+       return (*fop_fd)->fops->LWS_FOP_CLOSE(fop_fd);
 }
 
 /**
@@ -4429,15 +4498,15 @@ lws_vfs_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
        return fop_fd->fops->LWS_FOP_WRITE(fop_fd, amount, buf, len);
 }
 
-/* these are the flatform file operations implmenetations... they can
+/* these are the platform file operations implementations... they can
  * be called directly and used in fops arrays
  */
 
 LWS_VISIBLE LWS_EXTERN lws_fop_fd_t
 _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
-                   lws_filepos_t *filelen, lws_fop_flags_t *flags);
+                   const char *vpath, lws_fop_flags_t *flags);
 LWS_VISIBLE LWS_EXTERN int
-_lws_plat_file_close(lws_fop_fd_t fop_fd);
+_lws_plat_file_close(lws_fop_fd_t *fop_fd);
 LWS_VISIBLE LWS_EXTERN lws_fileofs_t
 _lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset);
 LWS_VISIBLE LWS_EXTERN int
index 680dc56..e858b5a 100644 (file)
@@ -397,7 +397,7 @@ lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
 
 LWS_VISIBLE lws_fop_fd_t
 _lws_plat_file_open(struct lws_plat_file_ops *fops, const char *filename,
-                  lws_filepos_t *filelen, lws_fop_flags_t *flags)
+                   const char *vpath, lws_fop_flags_t *flags)
 {
        struct stat stat_buf;
        lws_fop_fd_t fop_fd;
@@ -414,9 +414,10 @@ _lws_plat_file_open(struct lws_plat_file_ops *fops, const char *filename,
 
        fop_fd->fops = fops;
        fop_fd->fd = ret;
+       fop_fd->flags = *flags;
        fop_fd->filesystem_priv = NULL; /* we don't use it */
-
-       *filelen = stat_buf.st_size;
+       fop_fd->pos = 0;
+       fop_fd->len = stat_buf.st_size;
 
        return fop_fd;
 
@@ -427,11 +428,12 @@ bail:
 }
 
 LWS_VISIBLE int
-_lws_plat_file_close(lws_fop_fd_t fops_fd)
+_lws_plat_file_close(lws_fop_fd_t *fops_fd)
 {
-       int fd = fops_fd->fd;
+       int fd = (*fops_fd)->fd;
 
-       free(fd);
+       free(*fops_fd);
+       *fops_fd = NULL;
 
        return close(fd);
 }
@@ -453,7 +455,7 @@ _lws_plat_file_read(lws_fop_fd_t fops_fd, lws_filepos_t *amount,
                *amount = 0;
                return -1;
        }
-
+       fop_fd->pos += n;
        *amount = n;
 
        return 0;
@@ -470,7 +472,7 @@ _lws_plat_file_write(lws_fop_fd_t fops_fd, lws_filepos_t *amount,
                *amount = 0;
                return -1;
        }
-
+       fop_fd->pos += n;
        *amount = n;
 
        return 0;
index f6051d0..20379bc 100644 (file)
@@ -263,14 +263,13 @@ lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
 
 LWS_VISIBLE lws_fop_fd_t
 _lws_plat_file_open(lws_plat_file_open(struct lws_plat_file_ops *fops,
-                   const char *filename, lws_filepos_t *filelen,
-                   lws_fop_flags_t *flags)
+                   const char *filename, lws_fop_flags_t *flags)
 {
        return NULL;
 }
 
 LWS_VISIBLE int
-_lws_plat_file_close(lws_fop_fd_t fop_fd)
+_lws_plat_file_close(lws_fop_fd_t *fop_fd)
 {
        return 0;
 }
index 4ab2eea..49c3d21 100644 (file)
@@ -611,7 +611,7 @@ lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
 
 LWS_VISIBLE lws_fop_fd_t
 _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
-                   lws_filepos_t *filelen, lws_fop_flags_t *flags)
+                   const char *vpath, lws_fop_flags_t *flags)
 {
        struct stat stat_buf;
        int ret = open(filename, (*flags) & LWS_FOP_FLAGS_MASK, 0664);
@@ -628,9 +628,11 @@ _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
                goto bail;
 
        fop_fd->fops = fops;
+       fop_fd->flags = *flags;
        fop_fd->fd = ret;
        fop_fd->filesystem_priv = NULL; /* we don't use it */
-       *filelen = stat_buf.st_size;
+       fop_fd->len = stat_buf.st_size;
+       fop_fd->pos = 0;
 
        return fop_fd;
 
@@ -640,18 +642,36 @@ bail:
 }
 
 LWS_VISIBLE int
-_lws_plat_file_close(lws_fop_fd_t fop_fd)
+_lws_plat_file_close(lws_fop_fd_t *fop_fd)
 {
-       int fd = fop_fd->fd;
+       int fd = (*fop_fd)->fd;
+
+       free(*fop_fd);
+       *fop_fd = NULL;
 
-       free(fop_fd);
        return close(fd);
 }
 
 LWS_VISIBLE lws_fileofs_t
 _lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
 {
-       return lseek(fop_fd->fd, offset, SEEK_CUR);
+       lws_fileofs_t r;
+
+       if (offset > 0 && offset > fop_fd->len - fop_fd->pos)
+               offset = fop_fd->len - fop_fd->pos;
+
+       if ((lws_fileofs_t)fop_fd->pos + offset < 0)
+               offset = -fop_fd->pos;
+
+       r = lseek(fop_fd->fd, offset, SEEK_CUR);
+
+       if (r >= 0)
+               fop_fd->pos = r;
+       else
+               lwsl_err("error seeking from cur %ld, offset %ld\n",
+                        fop_fd->pos, offset);
+
+       return r;
 }
 
 LWS_VISIBLE int
@@ -665,7 +685,9 @@ _lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
                *amount = 0;
                return -1;
        }
-
+       fop_fd->pos += n;
+       lwsl_debug("%s: read %ld of req %ld, pos %ld, len %ld\n", __func__, n,
+                  (long)len, fop_fd->pos, fop_fd->len);
        *amount = n;
 
        return 0;
@@ -683,6 +705,7 @@ _lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
                return -1;
        }
 
+       fop_fd->pos += n;
        *amount = n;
 
        return 0;
index 117b69f..188f1a9 100644 (file)
@@ -523,7 +523,7 @@ lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
 
 LWS_VISIBLE lws_fop_fd_t
 _lws_plat_file_open(struct lws_plat_file_ops *fops, const char *filename,
-                  lws_filepos_t *filelen, lws_fop_flags_t *flags)
+                   const char *vpath, lws_fop_flags_t *flags)
 {
        HANDLE ret;
        WCHAR buf[MAX_PATH];
@@ -548,22 +548,23 @@ _lws_plat_file_open(struct lws_plat_file_ops *fops, const char *filename,
        fop_fd->fops = fops;
        fop_fd->fd = ret;
        fop_fd->filesystem_priv = NULL; /* we don't use it */
-
-       *filelen = GetFileSize(ret, NULL);
+       fop_fd->flags = *flags;
+       fop_fd->len = GetFileSize(ret, NULL);
+       fop_fd->pos = 0;
 
        return fop_fd;
 
 bail:
-       *filelen = 0;
        return NULL;
 }
 
 LWS_VISIBLE int
-_lws_plat_file_close(lws_fop_fd_t fop_fd)
+_lws_plat_file_close(lws_fop_fd_t *fop_fd)
 {
-       HANDLE fd = fop_fd->fd;
+       HANDLE fd = (*fop_fd)->fd;
 
-       free(fop_fd);
+       free(*fop_fd);
+       *fop_fd = NULL;
 
        CloseHandle((HANDLE)fd);
 
@@ -588,6 +589,7 @@ _lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
                return 1;
        }
 
+       fop_fd->pos += _amount;
        *amount = (unsigned long)_amount;
 
        return 0;
@@ -602,6 +604,8 @@ _lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
        (void)buf;
        (void)len;
 
+       fop_fd->pos += len;
+
        lwsl_err("%s: not implemented yet on this platform\n", __func__);
 
        return -1;
index 232246f..efc9573 100644 (file)
@@ -574,7 +574,7 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
                                          wsi->trunc_offset,
                                          wsi->trunc_len) < 0) {
                                lwsl_info("%s: closing\n", __func__);
-                               return -1;
+                               goto file_had_it;
                        }
                        continue;
                }
@@ -594,7 +594,7 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
                        if ((long)lws_vfs_file_seek_cur(wsi->u.http.fop_fd,
                                                   wsi->u.http.range.start -
                                                   wsi->u.http.filepos) < 0)
-                               return -1;
+                               goto file_had_it;
 
                        wsi->u.http.filepos = wsi->u.http.range.start;
 
@@ -634,7 +634,7 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
                }
 
                if (lws_vfs_file_read(wsi->u.http.fop_fd, &amount, p, poss) < 0)
-                       return -1; /* caller will close */
+                       goto file_had_it; /* caller will close */
                
                //lwsl_notice("amount %ld\n", amount);
 
@@ -656,7 +656,7 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
                                     wsi->vhost->protocols[(int)wsi->protocol_interpret_idx].callback, wsi,
                                     LWS_CALLBACK_PROCESS_HTML,
                                     wsi->user_space, &args, 0) < 0)
-                                       return -1;
+                                       goto file_had_it;
                                n = args.len;
                                p = (unsigned char *)args.p;
                        } else
@@ -678,7 +678,7 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
                                        LWS_WRITE_HTTP
                                );
                        if (m < 0)
-                               return -1;
+                               goto file_had_it;
 
                        wsi->u.http.filepos += amount;
 
@@ -703,7 +703,7 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
                                if (lws_vfs_file_seek_cur(wsi->u.http.fop_fd,
                                                           m - n) ==
                                                             (unsigned long)-1)
-                                       return -1;
+                                       goto file_had_it;
                        }
                }
 all_sent:
@@ -716,8 +716,7 @@ all_sent:
                     {
                        wsi->state = LWSS_HTTP;
                        /* we might be in keepalive, so close it off here */
-                       lws_vfs_file_close(wsi->u.http.fop_fd);
-                       wsi->u.http.fop_fd = NULL;
+                       lws_vfs_file_close(&wsi->u.http.fop_fd);
                        
                        lwsl_debug("file completed\n");
 
@@ -736,6 +735,11 @@ all_sent:
        lws_callback_on_writable(wsi);
 
        return 0; /* indicates further processing must be done */
+
+file_had_it:
+       lws_vfs_file_close(&wsi->u.http.fop_fd);
+
+       return -1;
 }
 
 #if LWS_POSIX
index 3431c97..80d0636 100644 (file)
@@ -439,57 +439,6 @@ extern "C" {
 #define SYSTEM_RANDOM_FILEPATH "/dev/urandom"
 #endif
 
-/**
- * Unzip library by Per Bothner.
- * Loosely based on Joonas Pihlajamaa's JUnzip.
- * Released into public domain. https://github.com/jokkebk/JUnzip
- * ------->
- */
-
-typedef struct jzfile {
-    unsigned char *start;
-    off_t length;
-    long position;
-    int numEntries;
-    uint32_t centralDirectoryOffset;
-} jzfile_t;
-
-#define zf_tell(ZF) ((ZF)->position)
-#define zf_available(ZF) ((ZF)->length - (ZF)->position)
-#define zf_current(ZF) ((ZF)->start + (ZF)->position)
-
-#define ZIP_LOCAL_FILE_HEADER_LENGTH 30
-
-typedef struct {
-    uint16_t compressionMethod;
-    uint32_t crc32;
-    uint32_t compressedSize;
-    uint32_t uncompressedSize;
-    long fileNameStart;
-    uint16_t fileNameLength;
-    uint16_t extraFieldLength; // unsupported
-    uint32_t offset;
-} jzfile_hdr_t;
-
-// Callback prototype for central and local file record reading functions
-typedef int (*jzcb_t)(jzfile_t *zip, int index, jzfile_hdr_t *header);
-
-// Read ZIP file end record. Will move within file.
-int jzReadEndRecord(jzfile_t *zip);
-
-// Read ZIP file global directory. Will move within file.
-// Callback is called for each record, until callback returns zero
-int jzReadCentralDirectory(jzfile_t *zip, jzcb_t callback);
-
-  // See to the start of the actual data of the given entry.
-int jzSeekData(jzfile_t *zip, jzfile_hdr_t *header);
-
-// Read data from file stream, described by header, to preallocated buffer
-// Return value is zlib coded, e.g. Z_OK, or error code
-int jzReadData(jzfile_t *zip, jzfile_hdr_t *header, void *buffer);
-
-/* <------ */
-
 enum lws_websocket_opcodes_07 {
        LWSWSOPC_CONTINUATION = 0,
        LWSWSOPC_TEXT_FRAME = 1,
@@ -860,7 +809,10 @@ struct lws_context {
        time_t last_ws_ping_pong_check_s;
        time_t time_up;
        const struct lws_plat_file_ops *fops;
-       struct lws_plat_file_ops fops_default[2];
+       struct lws_plat_file_ops fops_platform;
+#if defined(LWS_WITH_ZIP_FOPS)
+       struct lws_plat_file_ops fops_zip;
+#endif
        struct lws_context_per_thread pt[LWS_MAX_SMP];
        struct lws_conn_stats conn_stats;
 #ifdef _WIN32
@@ -947,6 +899,7 @@ struct lws_context {
        short server_string_len;
        unsigned short ws_ping_pong_interval;
        unsigned short deprecation_pending_listen_close_count;
+       uint8_t max_fi;
 };
 
 #define lws_get_context_protocol(ctx, x) ctx->vhost_list->protocols[x]
@@ -2005,6 +1958,10 @@ void lws_free(void *p);
 #define lws_free_set_NULL(P)   do { lws_realloc(P, 0); (P) = NULL; } while(0)
 #endif
 
+const struct lws_plat_file_ops *
+lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path,
+                   const char **vpath);
+
 /* lws_plat_ */
 LWS_EXTERN void
 lws_plat_delete_socket_from_fds(struct lws_context *context,
index 73be40a..32d972e 100644 (file)
@@ -312,6 +312,22 @@ lws_get_mimetype(const char *file, const struct lws_http_mount *m)
 
        return NULL;
 }
+static lws_fop_flags_t
+lws_vfs_prepare_flags(struct lws *wsi)
+{
+       lws_fop_flags_t f = 0;
+
+       if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING))
+               return f;
+
+       if (strstr(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING),
+                  "gzip")) {
+               lwsl_info("client indicates GZIP is acceptable\n");
+               f |= LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP;
+       }
+
+       return f;
+}
 
 static int
 lws_http_serve(struct lws *wsi, char *uri, const char *origin,
@@ -319,8 +335,11 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
 {
        const struct lws_protocol_vhost_options *pvo = m->interpret;
        struct lws_process_html_args args;
-       const char *mimetype;
-#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32)
+       const char *mimetype, *vpath;
+#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP8266) && \
+    !defined(LWS_WITH_ESP32)
+       const struct lws_plat_file_ops *fops;
+       lws_fop_flags_t fflags = LWS_O_RDONLY;
        struct stat st;
        int spin = 0;
 #endif
@@ -334,15 +353,37 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
 
        lws_snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri);
 
-#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32)
+       fflags |= lws_vfs_prepare_flags(wsi);
+
+#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP8266) && \
+    !defined(LWS_WITH_ESP32)
        do {
                spin++;
+               fops = lws_vfs_select_fops(wsi->context->fops, path, &vpath);
+
+               if (wsi->u.http.fop_fd)
+                       lws_vfs_file_close(&wsi->u.http.fop_fd);
+
+               wsi->u.http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
+                                                       path, vpath, &fflags);
+               if (!wsi->u.http.fop_fd) {
+                       lwsl_err("Unable to open '%s'\n", path);
+
+                       return -1;
+               }
 
-               if (stat(path, &st)) {
+               /* if it can't be statted, don't try */
+               if (fflags & LWS_FOP_FLAG_VIRTUAL)
+                       break;
+
+               if (fstat(wsi->u.http.fop_fd->fd, &st)) {
                        lwsl_info("unable to stat %s\n", path);
                        goto bail;
                }
 
+               wsi->u.http.fop_fd->mod_time = (uint32_t)st.st_mtime;
+               fflags |= LWS_FOP_FLAG_MOD_TIME_VALID;
+
                lwsl_debug(" %s mode %d\n", path, S_IFMT & st.st_mode);
 #if !defined(WIN32) && LWS_POSIX
                if ((S_IFMT & st.st_mode) == S_IFLNK) {
@@ -367,8 +408,9 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
        if (spin == 5)
                lwsl_err("symlink loop %s \n", path);
 
-       n = sprintf(sym, "%08lX%08lX", (unsigned long)st.st_size,
-                                  (unsigned long)st.st_mtime);
+       n = sprintf(sym, "%08lX%08lX",
+                   (unsigned long)lws_vfs_get_length(wsi->u.http.fop_fd),
+                   (unsigned long)lws_vfs_get_mod_time(wsi->u.http.fop_fd));
 
        /* disable ranges if IF_RANGE token invalid */
 
@@ -382,7 +424,8 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
                 * he thinks he has some version of it already,
                 * check if the tag matches
                 */
-               if (!strcmp(sym, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH))) {
+               if (!strcmp(sym, lws_hdr_simple_ptr(wsi,
+                                       WSI_TOKEN_HTTP_IF_NONE_MATCH))) {
 
                        lwsl_debug("%s: ETAG match %s %s\n", __func__,
                                   uri, origin);
@@ -407,6 +450,8 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
                                return -1;
                        }
 
+                       lws_vfs_file_close(&wsi->u.http.fop_fd);
+
                        return lws_http_transaction_completed(wsi);
                }
        }
@@ -2060,7 +2105,7 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 
                len = lws_ssl_capable_read(wsi, pt->serv_buf,
                                           context->pt_serv_buf_size);
-               lwsl_notice("%s: wsi %p read %d\r\n", __func__, wsi, len);
+//             lwsl_notice("%s: wsi %p read %d\r\n", __func__, wsi, len);
                switch (len) {
                case 0:
                        lwsl_info("%s: read 0 len\n", __func__);
@@ -2228,39 +2273,33 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
        unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
        unsigned long computed_total_content_length;
        int ret = 0, cclen = 8, n = HTTP_STATUS_OK;
-       lws_fop_flags_t fflags = O_RDONLY;
+       lws_fop_flags_t fflags = LWS_O_RDONLY;
 #if defined(LWS_WITH_RANGES)
        int ranges;
 #endif
+       const struct lws_plat_file_ops *fops;
+       const char *vpath;
 
-       if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING))
-               if (strstr("gzip",  lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING)) &&
-                   strstr("deflate",  lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING))) {
-                       lwsl_debug("client indicates GZIP is acceptable\n");
-                       fflags |= LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP;
-               }
-
-
-       wsi->u.http.fop_fd = lws_vfs_file_open(lws_select_fops_by_vfs_path(
-                                               wsi->context, file), file,
-                                               &wsi->u.http.filelen,
-                                               &fflags);
+       /*
+        * We either call the platform fops .open with first arg platform fops,
+        * or we call fops_zip .open with first arg platform fops, and fops_zip
+        * open will decide whether to switch to fops_zip or stay with fops_def.
+        *
+        * If wsi->u.http.fop_fd is already set, the caller already opened it
+        */
        if (!wsi->u.http.fop_fd) {
-               lwsl_err("Unable to open '%s'\n", file);
-
-               return -1;
-       }
-       computed_total_content_length = wsi->u.http.filelen;
+               fops = lws_vfs_select_fops(wsi->context->fops, file, &vpath);
+               fflags |= lws_vfs_prepare_flags(wsi);
+               wsi->u.http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
+                                                       file, vpath, &fflags);
+               if (!wsi->u.http.fop_fd) {
+                       lwsl_err("Unable to open '%s'\n", file);
 
-       if ((fflags & (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP |
-                      LWS_FOP_FLAG_COMPR_IS_GZIP)) ==
-           (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP | LWS_FOP_FLAG_COMPR_IS_GZIP)) {
-               if (lws_add_http_header_by_token(wsi,
-                       WSI_TOKEN_HTTP_CONTENT_ENCODING,
-                       (unsigned char *)"gzip, deflate", 13, &p, end))
                        return -1;
-               lwsl_debug("file is being provided in gzip\n");
+               }
        }
+       wsi->u.http.filelen = lws_vfs_get_length(wsi->u.http.fop_fd);
+       computed_total_content_length = wsi->u.http.filelen;
 
 #if defined(LWS_WITH_RANGES)
        ranges = lws_ranges_init(wsi, rp, wsi->u.http.filelen);
@@ -2279,6 +2318,8 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
                if (lws_http_transaction_completed(wsi))
                        return -1; /* <0 means just hang up */
 
+               lws_vfs_file_close(&wsi->u.http.fop_fd);
+
                return 0; /* == 0 means we dealt with the transaction complete */
        }
        if (ranges)
@@ -2288,6 +2329,16 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
        if (lws_add_http_header_status(wsi, n, &p, end))
                return -1;
 
+       if ((wsi->u.http.fop_fd->flags & (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP |
+                      LWS_FOP_FLAG_COMPR_IS_GZIP)) ==
+           (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP | LWS_FOP_FLAG_COMPR_IS_GZIP)) {
+               if (lws_add_http_header_by_token(wsi,
+                       WSI_TOKEN_HTTP_CONTENT_ENCODING,
+                       (unsigned char *)"gzip", 4, &p, end))
+                       return -1;
+               lwsl_info("file is being provided in gzip\n");
+       }
+
 #if defined(LWS_WITH_RANGES)
        if (ranges < 2 && content_type && content_type[0])
                if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
diff --git a/test-server/candide.zip b/test-server/candide.zip
new file mode 100644 (file)
index 0000000..82a6619
Binary files /dev/null and b/test-server/candide.zip differ
index 154674b..3b3029d 100644 (file)
@@ -292,13 +292,14 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
                        p = buffer + LWS_PRE;
                        end = p + sizeof(buffer) - LWS_PRE;
 
-                       pss->fop_fd = lws_vfs_file_open(lws_get_fops(lws_get_context(wsi)),
-                                       leaf_path, &file_len, &flags);
-
+                       pss->fop_fd = lws_vfs_file_open(
+                                       lws_get_fops(lws_get_context(wsi)),
+                                       leaf_path, &flags);
                        if (!pss->fop_fd) {
                                lwsl_err("failed to open file %s\n", leaf_path);
                                return -1;
                        }
+                       file_len = lws_vfs_get_length(pss->fop_fd);
 
                        /*
                         * we will send a big jpeg file, but it could be
@@ -346,7 +347,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
                                      p - (buffer + LWS_PRE),
                                      LWS_WRITE_HTTP_HEADERS);
                        if (n < 0) {
-                               lws_vfs_file_close(pss->fop_fd);
+                               lws_vfs_file_close(&pss->fop_fd);
                                return -1;
                        }
                        /*
@@ -588,12 +589,12 @@ later:
                lws_callback_on_writable(wsi);
                break;
 penultimate:
-               lws_vfs_file_close(pss->fop_fd);
+               lws_vfs_file_close(&pss->fop_fd);
                pss->fop_fd = NULL;
                goto try_to_reuse;
 
 bail:
-               lws_vfs_file_close(pss->fop_fd);
+               lws_vfs_file_close(&pss->fop_fd);
 
                return -1;
 
index b27b2ed..e1424ce 100644 (file)
@@ -150,16 +150,16 @@ static const struct lws_extension exts[] = {
  * compressed files without decompressing the whole archive)
  */
 static lws_fop_fd_t
-test_server_fops_open(struct lws_plat_file_ops *fops, const char *filename,
-                  lws_filepos_t *filelen, lws_fop_flags_t *flags)
+test_server_fops_open(const struct lws_plat_file_ops *fops,
+                     const char *vfs_path, const char *vpath,
+                     lws_fop_flags_t *flags)
 {
        lws_fop_fd_t n;
 
        /* call through to original platform implementation */
-       n = fops_plat.open(fops, filename, filelen, flags);
+       n = fops_plat.open(fops, vfs_path, vpath, flags);
 
-       lwsl_notice("%s: opening %s, ret %p, len %lu\n", __func__, filename,
-                   n, *filelen);
+       lwsl_notice("%s: opening %s, ret %p\n", __func__, vfs_path, n);
 
        return n;
 }
index 7e5a559..96e00a5 100644 (file)
@@ -67,11 +67,33 @@ static const struct lws_extension exts[] = {
 };
 
 /*
- * mount a handler for a section of the URL space
+ * mount handlers for sections of the URL space
  */
 
+static const struct lws_http_mount mount_ziptest = {
+       NULL,                   /* linked-list pointer to next*/
+       "/ziptest",             /* mountpoint in URL namespace on this vhost */
+       LOCAL_RESOURCE_PATH"/candide.zip",      /* handler */
+       NULL,   /* default filename if none given */
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       LWSMPRO_FILE,   /* origin points to a callback */
+       8,                      /* strlen("/ziptest"), ie length of the mountpoint */
+       NULL,
+
+       { NULL, NULL } // sentinel
+};
+
 static const struct lws_http_mount mount_post = {
-       NULL,           /* linked-list pointer to next*/
+       (struct lws_http_mount *)&mount_ziptest, /* linked-list pointer to next*/
        "/formtest",            /* mountpoint in URL namespace on this vhost */
        "protocol-post-demo",   /* handler */
        NULL,   /* default filename if none given */
index 93fb116..74c37da 100644 (file)
@@ -122,17 +122,20 @@ static struct lws_protocols protocols[] = {
  */
 static lws_fop_fd_t
 test_server_fops_open(const struct lws_plat_file_ops *fops,
-                    const char *filename,
-                    lws_filepos_t *filelen,
+                    const char *vfs_path, const char *vpath,
                     lws_fop_flags_t *flags)
 {
        lws_fop_fd_t fop_fd;
 
        /* call through to original platform implementation */
-       fop_fd = fops_plat.open(fops, filename, filelen, flags);
-
-       lwsl_info("%s: opening %s, ret %p, len %lu\n", __func__, filename,
-                       fop_fd, (long)*filelen);
+       fop_fd = fops_plat.open(fops, vfs_path, vpath, flags);
+
+       if (fop_fd)
+               lwsl_info("%s: opening %s, ret %p, len %lu\n", __func__,
+                               vfs_path, fop_fd,
+                               (long)lws_vfs_get_length(fop_fd));
+       else
+               lwsl_info("%s: open %s failed\n", __func__, vfs_path);
 
        return fop_fd;
 }