if (LWS_WITH_ZIP_FOPS)
list(APPEND SOURCES
- lib/junzip.c)
+ lib/fops-zip.c)
endif()
# Add helper files for Windows.
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")
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
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.
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
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) {
}
#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;
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
/* 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)
--- /dev/null
+/*
+ * 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,
+};
+++ /dev/null
-// 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;
-}
"cgi://",
">http://",
">https://",
- "callback://"
+ "callback://",
+ "gzip://",
};
if (!a->fresh_mount)
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;
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
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;
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
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
*
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 *
#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
* 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 ---^
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);
//@}
#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)
#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 */
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 */
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
* \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);
}
/**
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
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;
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;
}
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);
}
*amount = 0;
return -1;
}
-
+ fop_fd->pos += n;
*amount = n;
return 0;
*amount = 0;
return -1;
}
-
+ fop_fd->pos += n;
*amount = n;
return 0;
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;
}
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);
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;
}
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
*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;
return -1;
}
+ fop_fd->pos += n;
*amount = n;
return 0;
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];
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);
return 1;
}
+ fop_fd->pos += _amount;
*amount = (unsigned long)_amount;
return 0;
(void)buf;
(void)len;
+ fop_fd->pos += len;
+
lwsl_err("%s: not implemented yet on this platform\n", __func__);
return -1;
wsi->trunc_offset,
wsi->trunc_len) < 0) {
lwsl_info("%s: closing\n", __func__);
- return -1;
+ goto file_had_it;
}
continue;
}
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;
}
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);
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
LWS_WRITE_HTTP
);
if (m < 0)
- return -1;
+ goto file_had_it;
wsi->u.http.filepos += amount;
if (lws_vfs_file_seek_cur(wsi->u.http.fop_fd,
m - n) ==
(unsigned long)-1)
- return -1;
+ goto file_had_it;
}
}
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");
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
#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,
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
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]
#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,
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,
{
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
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) {
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 */
* 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);
return -1;
}
+ lws_vfs_file_close(&wsi->u.http.fop_fd);
+
return lws_http_transaction_completed(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__);
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);
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)
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,
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
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;
}
/*
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;
* 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;
}
};
/*
- * 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 */
*/
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;
}