From 4e442b774306134db3bae13cbed88623290ee69c Mon Sep 17 00:00:00 2001
From: Andy Green <andy.green@linaro.org>
Date: Thu, 10 Dec 2015 07:58:58 +0800
Subject: [PATCH] lws_plat_fd implement platform default handlers

This is a rewrite of the patch from Soapyman here

https://github.com/warmcat/libwebsockets/pull/363

The main changes compared to Soapyman's original patch are

 - There's no new stuff in the info struct user code does any overrides
   it may want to do explicitly after lws_context_create returns

 - User overrides for file ops can call through (subclass) to the original
   platform implementation using lws_get_fops_plat()

 - A typedef is provided for plat-specific fd type

 - Public helpers are provided to allow user code to be platform-independent
   about file access, using the lws platform file operations underneath:

static inline lws_filefd_type
lws_plat_file_open(struct lws_plat_file_ops *fops, const char *filename,
		   unsigned long *filelen, int flags)

static inline int
lws_plat_file_close(struct lws_plat_file_ops *fops, lws_filefd_type fd)

static inline unsigned long
lws_plat_file_seek_cur(struct lws_plat_file_ops *fops, lws_filefd_type fd,
		       long offset_from_cur_pos)

static inline int
lws_plat_file_read(struct lws_plat_file_ops *fops, lws_filefd_type fd,
		   unsigned long *amount, unsigned char *buf, unsigned long len)

static inline int
lws_plat_file_write(struct lws_plat_file_ops *fops, lws_filefd_type fd,
		    unsigned long *amount, unsigned char *buf, unsigned long len)

There's example documentation and implementation in the test server.

Signed-off-by: Andy Green <andy.green@linaro.org>
---
 README.coding.md               |  40 +++++++++
 changelog                      |  45 +++++++++++
 lib/libwebsockets.c            |   7 +-
 lib/libwebsockets.h            |  56 +++++++++++++
 lib/lws-plat-mbed3.c           |  80 ++++++++++++++----
 lib/lws-plat-unix.c            | 138 +++++++++++++++++++++----------
 lib/lws-plat-win.c             | 143 ++++++++++++++++++++++-----------
 lib/output.c                   |  20 +++--
 lib/private-libwebsockets.h    |  32 +-------
 lib/server.c                   |   6 +-
 test-server/test-server-http.c |  40 ++++-----
 test-server/test-server.c      |  29 +++++++
 test-server/test-server.h      |  26 +++---
 13 files changed, 483 insertions(+), 179 deletions(-)

diff --git a/README.coding.md b/README.coding.md
index 28be02c5..c2a5f3b3 100644
--- a/README.coding.md
+++ b/README.coding.md
@@ -276,3 +276,43 @@ After attempting the connection and getting back a non-`NULL` `wsi` you should
 loop calling `lws_service()` until one of the above callbacks occurs.
 
 As usual, see [test-client.c](test-server/test-client.c) for example code.
+
+Lws platform-independent file access apis
+-----------------------------------------
+
+lws now exposes his internal platform file abstraction in a way that can be
+both used by user code to make it platform-agnostic, and be overridden or
+subclassed by user code.  This allows things like handling the URI "directory
+space" as a virtual filesystem that may or may not be backed by a regular
+filesystem.  One example use is serving files from inside large compressed
+archive storage without having to unpack anything except the file being
+requested.
+
+The test server shows how to use it, basically the platform-specific part of
+lws prepares a file operations structure that lives in the lws context.
+
+The user code can get a pointer to the file operations struct
+
+LWS_VISIBLE LWS_EXTERN struct lws_plat_file_ops *
+`lws_get_fops`(struct lws_context *context);
+
+and then can use it with helpers to also leverage these platform-independent
+file handling apis
+
+static inline lws_filefd_type
+`lws_plat_file_open`(struct lws_plat_file_ops *fops, const char *filename, unsigned long *filelen, int flags)
+
+static inline int
+`lws_plat_file_close`(struct lws_plat_file_ops *fops, lws_filefd_type fd)
+
+static inline unsigned long
+`lws_plat_file_seek_cur`(struct lws_plat_file_ops *fops, lws_filefd_type fd, long offset_from_cur_pos)
+
+static inline int
+`lws_plat_file_read`(struct lws_plat_file_ops *fops, lws_filefd_type fd, unsigned long *amount, unsigned char *buf, unsigned long len)
+
+static inline int
+`lws_plat_file_write`(struct lws_plat_file_ops *fops, lws_filefd_type fd, unsigned long *amount, unsigned char *buf, unsigned long len)
+		    
+The user code can also override or subclass the file operations, to either
+wrap or replace them.  An example is shown in test server.
diff --git a/changelog b/changelog
index 583f8dd1..3fc66e16 100644
--- a/changelog
+++ b/changelog
@@ -1,6 +1,51 @@
 Changelog
 ---------
 
+User api additions
+------------------
+
+lws now exposes his internal platform file abstraction in a way that can be
+both used by user code to make it platform-agnostic, and be overridden or
+subclassed by user code.  This allows things like handling the URI "directory
+space" as a virtual filesystem that may or may not be backed by a regular
+filesystem.  One example use is serving files from inside large compressed
+archive storage without having to unpack anything except the file being
+requested.
+
+The test server shows how to use it, basically the platform-specific part of
+lws prepares a file operations structure that lives in the lws context.
+
+The user code can get a pointer to the file operations struct
+
+LWS_VISIBLE LWS_EXTERN struct lws_plat_file_ops *
+lws_get_fops(struct lws_context *context);
+
+and then can use it with helpers to also leverage these platform-independent
+file handling apis
+
+static inline lws_filefd_type
+lws_plat_file_open(struct lws_plat_file_ops *fops, const char *filename,
+		   unsigned long *filelen, int flags)
+
+static inline int
+lws_plat_file_close(struct lws_plat_file_ops *fops, lws_filefd_type fd)
+
+static inline unsigned long
+lws_plat_file_seek_cur(struct lws_plat_file_ops *fops, lws_filefd_type fd,
+		       long offset_from_cur_pos)
+
+static inline int
+lws_plat_file_read(struct lws_plat_file_ops *fops, lws_filefd_type fd,
+		   unsigned long *amount, unsigned char *buf, unsigned long len)
+
+static inline int
+lws_plat_file_write(struct lws_plat_file_ops *fops, lws_filefd_type fd,
+		    unsigned long *amount, unsigned char *buf, unsigned long len)
+		    
+The user code can also override or subclass the file operations, to either
+wrap or replace them.  An example is shown in test server.
+
+
 User api changes
 ----------------
 
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 16b50273..1cb66843 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -73,7 +73,7 @@ lws_close_and_free_session(struct lws_context *context,
 	if (wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED &&
 	    wsi->u.http.fd != LWS_INVALID_FILE) {
 		lwsl_debug("closing http file\n");
-		compatible_file_close(wsi->u.http.fd);
+		lws_plat_file_close(&context->fops, wsi->u.http.fd);
 		wsi->u.http.fd = LWS_INVALID_FILE;
 		context->protocols[0].callback(context, wsi,
 			LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0);
@@ -929,6 +929,11 @@ lws_union_transition(struct lws *wsi, enum connection_mode mode)
 	wsi->mode = mode;
 }
 
+LWS_VISIBLE struct lws_plat_file_ops *
+lws_get_fops(struct lws_context *context)
+{
+	return &context->fops;
+}
 
 #ifdef LWS_WITH_OLD_API_WRAPPERS
 
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 3ef4f188..f930485d 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -177,9 +177,13 @@ extern "C" {
 #else
 #define LWS_EXTERN
 #endif
+	
+#define LWS_INVALID_FILE INVALID_HANDLE_VALUE
 
 #else /* NOT WIN32 */
 #include <unistd.h>
+	
+#define LWS_INVALID_FILE -1
 
 #ifndef MBED_OPERATORS
 #include <poll.h>
@@ -296,6 +300,9 @@ LWS_VISIBLE LWS_EXTERN void lwsl_hexdump(void *buf, size_t len);
 /* extra parameter introduced in 917f43ab821 */
 #define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_LEN
 
+/* File operations stuff exists */
+#define LWS_FEATURE_FOPS
+
 /*
  * NOTE: These public enums are part of the abi.  If you want to add one,
  * add it at where specified so existing users are unaffected.
@@ -1626,6 +1633,55 @@ LWS_VISIBLE LWS_EXTERN int
 lws_hdr_copy(struct lws *wsi, char *dest, int len,
 	     enum lws_token_indexes h);
 
+/* get the active file operations struct */
+LWS_VISIBLE LWS_EXTERN struct lws_plat_file_ops *
+lws_get_fops(struct lws_context *context);
+
+/*
+ * File Operations access helpers
+ *
+ * usually the first argument will be lws_get_fops(context)
+ * If so, then it calls the platform handler or user overrides where present
+ * (as defined in info->fops)
+ *
+ * The advantage from all this is user code can be portable for file operations
+ * without having to deal with differences between platforms.
+ */
+
+static inline lws_filefd_type
+lws_plat_file_open(struct lws_plat_file_ops *fops, const char *filename,
+		   unsigned long *filelen, int flags)
+{
+	return fops->open(filename, filelen, flags);
+}
+
+static inline int
+lws_plat_file_close(struct lws_plat_file_ops *fops, lws_filefd_type fd)
+{
+	return fops->close(fd);
+}
+
+static inline unsigned long
+lws_plat_file_seek_cur(struct lws_plat_file_ops *fops, lws_filefd_type fd,
+		       long offset_from_cur_pos)
+{
+	return fops->seek_cur(fd, offset_from_cur_pos);
+}
+
+static inline int
+lws_plat_file_read(struct lws_plat_file_ops *fops, lws_filefd_type fd,
+		   unsigned long *amount, unsigned char *buf, unsigned long len)
+{
+	return fops->read(fd, amount, buf, len);
+}
+
+static inline int
+lws_plat_file_write(struct lws_plat_file_ops *fops, lws_filefd_type fd,
+		    unsigned long *amount, unsigned char *buf, unsigned long len)
+{
+	return fops->write(fd, amount, buf, len);
+}
+
 /*
  * Note: this is not normally needed as a user api.  It's provided in case it is
  * useful when integrating with other app poll loop service code.
diff --git a/lib/lws-plat-mbed3.c b/lib/lws-plat-mbed3.c
index 372cbbb6..1fa7db83 100644
--- a/lib/lws-plat-mbed3.c
+++ b/lib/lws-plat-mbed3.c
@@ -105,14 +105,6 @@ lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
 	(void)info;
 }
 
-LWS_VISIBLE int
-lws_plat_init(struct lws_context *context,
-	struct lws_context_creation_info *info)
-{
-	return 0;
-}
-
-
 LWS_VISIBLE int
 lws_plat_context_early_init(void)
 {
@@ -138,14 +130,6 @@ lws_plat_service_periodic(struct lws_context *context)
 	(void)context;
 }
 
-LWS_VISIBLE int
-lws_plat_open_file(const char* filename, unsigned long* filelen)
-{
-	(void)filename;
-	(void)filelen;
-	return LWS_INVALID_FILE;
-}
-
 LWS_VISIBLE const char *
 lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
 {
@@ -172,4 +156,66 @@ delete_from_fd(struct lws_context *context, lws_sockfd_type fd)
 	(void)fd;
 
 	return 1;
-}
\ No newline at end of file
+}
+
+static lws_filefd_type
+_lws_plat_file_open(const char *filename, unsigned long *filelen, int flags)
+{
+	(void)filename;
+	(void)filelen;
+	(void)flags;
+	return -1;
+}
+
+static int
+_lws_plat_file_close(lws_filefd_type fd)
+{
+	(void)fd;
+	return -1;
+}
+
+unsigned long
+_lws_plat_file_seek_cur(lws_filefd_type fd, long offset)
+{
+	(void)fd;
+	(void)offset;
+
+	return -1;
+}
+
+static int
+_lws_plat_file_read(lws_filefd_type fd, unsigned long *amount,
+		    unsigned char* buf, unsigned long* len)
+{
+	(void)amount;
+	(void)fd;
+	(void)buf;
+	(void)len;
+
+	return -1;
+}
+
+static int
+_lws_plat_file_write(lws_filefd_type fd, unsigned long *amount,
+		     unsigned char* buf, unsigned long len)
+{
+	(void)amount;
+	(void)fd;
+	(void)buf;
+	(void)len;
+
+	return -1;
+}
+
+LWS_VISIBLE int
+lws_plat_init(struct lws_context *context,
+	      struct lws_context_creation_info *info)
+{
+	context->fops.open	= _lws_plat_file_open;
+	context->fops.close	= _lws_plat_file_close;
+	context->fops.seek_cur	= _lws_plat_file_seek_cur;
+	context->fops.read	= _lws_plat_file_read;
+	context->fops.write	= _lws_plat_file_write;
+
+	return 0;
+}
diff --git a/lib/lws-plat-unix.c b/lib/lws-plat-unix.c
index 7673765c..a6898d86 100644
--- a/lib/lws-plat-unix.c
+++ b/lib/lws-plat-unix.c
@@ -271,43 +271,6 @@ lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
 
 }
 
-LWS_VISIBLE int
-lws_plat_init(struct lws_context *context,
-	      struct lws_context_creation_info *info)
-{
-	context->lws_lookup = lws_zalloc(sizeof(struct lws *) * context->max_fds);
-	if (context->lws_lookup == NULL) {
-		lwsl_err(
-		  "Unable to allocate lws_lookup array for %d connections\n",
-							      context->max_fds);
-		return 1;
-	}
-
-	context->fd_random = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
-	if (context->fd_random < 0) {
-		lwsl_err("Unable to open random device %s %d\n",
-				    SYSTEM_RANDOM_FILEPATH, context->fd_random);
-		return 1;
-	}
-
-	if (lws_libev_init_fd_table(context))
-		/* libev handled it instead */
-		return 0;
-
-	if (pipe(context->dummy_pipe_fds)) {
-		lwsl_err("Unable to create pipe\n");
-		return 1;
-	}
-
-	/* use the read end of pipe as first item */
-	context->fds[0].fd = context->dummy_pipe_fds[0];
-	context->fds[0].events = LWS_POLLIN;
-	context->fds[0].revents = 0;
-	context->fds_count = 1;
-
-	return 0;
-}
-
 static void sigpipe_handler(int x)
 {
 }
@@ -446,11 +409,17 @@ lws_plat_change_pollfd(struct lws_context *context,
 	return 0;
 }
 
-LWS_VISIBLE int
-lws_plat_open_file(const char* filename, unsigned long* filelen)
+LWS_VISIBLE const char *
+lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
+{
+	return inet_ntop(af, src, dst, cnt);
+}
+
+static lws_filefd_type
+_lws_plat_file_open(const char *filename, unsigned long *filelen, int flags)
 {
 	struct stat stat_buf;
-	int ret = open(filename, O_RDONLY);
+	int ret = open(filename, flags, 0664);
 
 	if (ret < 0)
 		return LWS_INVALID_FILE;
@@ -463,8 +432,91 @@ lws_plat_open_file(const char* filename, unsigned long* filelen)
 	return ret;
 }
 
-LWS_VISIBLE const char *
-lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
+static int
+_lws_plat_file_close(lws_filefd_type fd)
 {
-	return inet_ntop(af, src, dst, cnt);
+	return close(fd);
+}
+
+unsigned long
+_lws_plat_file_seek_cur(lws_filefd_type fd, long offset)
+{
+	return lseek(fd, offset, SEEK_CUR);
+}
+
+static int
+_lws_plat_file_read(lws_filefd_type fd, unsigned long *amount,
+		    unsigned char *buf, unsigned long len)
+{
+	long n;
+
+	n = read((int)fd, buf, len);
+	if (n == -1) {
+		*amount = 0;
+		return -1;
+	}
+
+	*amount = n;
+
+	return 0;
+}
+
+static int
+_lws_plat_file_write(lws_filefd_type fd, unsigned long *amount,
+		     unsigned char *buf, unsigned long len)
+{
+	long n;
+
+	n = write((int)fd, buf, len);
+	if (n == -1) {
+		*amount = 0;
+		return -1;
+	}
+
+	*amount = n;
+
+	return 0;
+}
+
+LWS_VISIBLE int
+lws_plat_init(struct lws_context *context,
+	      struct lws_context_creation_info *info)
+{
+	context->lws_lookup = lws_zalloc(sizeof(struct lws *) * context->max_fds);
+	if (context->lws_lookup == NULL) {
+		lwsl_err(
+		  "Unable to allocate lws_lookup array for %d connections\n",
+							      context->max_fds);
+		return 1;
+	}
+
+	context->fd_random = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
+	if (context->fd_random < 0) {
+		lwsl_err("Unable to open random device %s %d\n",
+				    SYSTEM_RANDOM_FILEPATH, context->fd_random);
+		return 1;
+	}
+
+	if (lws_libev_init_fd_table(context))
+		/* libev handled it instead */
+		return 0;
+
+	if (pipe(context->dummy_pipe_fds)) {
+		lwsl_err("Unable to create pipe\n");
+		return 1;
+	}
+
+	/* use the read end of pipe as first item */
+	context->fds[0].fd = context->dummy_pipe_fds[0];
+	context->fds[0].events = LWS_POLLIN;
+	context->fds[0].revents = 0;
+	context->fds_count = 1;
+
+	context->fops.open	= _lws_plat_file_open;
+	context->fops.close	= _lws_plat_file_close;
+	context->fops.seek_cur	= _lws_plat_file_seek_cur;
+	context->fops.read	= _lws_plat_file_read;
+	context->fops.write	= _lws_plat_file_write;
+
+	return 0;
 }
diff --git a/lib/lws-plat-win.c b/lib/lws-plat-win.c
index 43b7c6a4..f7afd99b 100644
--- a/lib/lws-plat-win.c
+++ b/lib/lws-plat-win.c
@@ -256,35 +256,6 @@ lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
 {
 }
 
-LWS_VISIBLE int
-lws_plat_init(struct lws_context *context,
-	      struct lws_context_creation_info *info)
-{
-	int i;
-
-	for (i = 0; i < FD_HASHTABLE_MODULUS; i++) {
-		context->fd_hashtable[i].wsi = lws_zalloc(sizeof(struct lws*) * context->max_fds);
-
-		if (!context->fd_hashtable[i].wsi) {
-			return -1;
-		}
-	}
-
-	context->events = lws_malloc(sizeof(WSAEVENT) * (context->max_fds + 1));
-	if (context->events == NULL) {
-		lwsl_err("Unable to allocate events array for %d connections\n",
-			context->max_fds);
-		return 1;
-	}
-
-	context->fds_count = 0;
-	context->events[0] = WSACreateEvent();
-
-	context->fd_random = 0;
-
-	return 0;
-}
-
 LWS_VISIBLE int
 lws_plat_context_early_init(void)
 {
@@ -393,23 +364,6 @@ lws_plat_change_pollfd(struct lws_context *context,
 	return 1;
 }
 
-LWS_VISIBLE HANDLE
-lws_plat_open_file(const char* filename, unsigned long* filelen)
-{
-	HANDLE ret;
-	WCHAR buffer[MAX_PATH];
-
-	MultiByteToWideChar(CP_UTF8, 0, filename, -1, buffer,
-				sizeof(buffer) / sizeof(buffer[0]));
-	ret = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ,
-				NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-
-	if (ret != LWS_INVALID_FILE)
-		*filelen = GetFileSize(ret, NULL);
-
-	return ret;
-}
-
 LWS_VISIBLE const char *
 lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
 { 
@@ -455,3 +409,100 @@ lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
 	lws_free(buffer);
 	return ok ? dst : NULL;
 }
+
+static lws_filefd_type
+_lws_plat_file_open(const char *filename, unsigned long *filelen, int flags)
+{
+	HANDLE ret;
+	WCHAR buf[MAX_PATH];
+
+	MultiByteToWideChar(CP_UTF8, 0, filename, -1, buf, ARRAY_SIZE(buf));
+	if (flags & O_RDONLY) {
+		ret = CreateFileW(buf, GENERIC_READ, FILE_SHARE_READ,
+			  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+	} else {
+		lwsl_err("%s: open for write not implemented\n", __func__);
+		*filelen = 0;
+		return LWS_INVALID_FILE;
+	}
+
+	if (ret != LWS_INVALID_FILE)
+		*filelen = GetFileSize(ret, NULL);
+
+	return ret;
+}
+
+static int
+_lws_plat_file_close(lws_filefd_type fd)
+{
+	CloseHandle((HANDLE)fd);
+
+	return 0;
+}
+
+static unsigned long
+_lws_plat_file_seek_cur(lws_filefd_type fd, long offset)
+{
+	return SetFilePointer((HANDLE)fd, offset, NULL, FILE_CURRENT);
+}
+
+static int
+_lws_plat_file_read(lws_filefd_type fd, unsigned long *amount,
+		   unsigned char* buf, unsigned long len)
+{
+	DWORD _amount;
+
+	if (!ReadFile((HANDLE)fd, buf, (DWORD)len, &_amount, NULL)) {
+		*amount = 0;
+
+		return 1;
+	}
+
+	*amount = (unsigned long)_amount;
+
+	return 0;
+}
+
+static int
+_lws_plat_file_write(lws_filefd_type fd, unsigned long *amount,
+		     unsigned char* buf, unsigned long len)
+{
+	lwsl_err("%s: not implemented on this platform\n", __func__);
+	
+	return -1;
+}
+
+LWS_VISIBLE int
+lws_plat_init(struct lws_context *context,
+	      struct lws_context_creation_info *info)
+{
+	int i;
+
+	for (i = 0; i < FD_HASHTABLE_MODULUS; i++) {
+		context->fd_hashtable[i].wsi =
+			lws_zalloc(sizeof(struct lws*) * context->max_fds);
+
+		if (!context->fd_hashtable[i].wsi)
+			return -1;
+	}
+
+	context->events = lws_malloc(sizeof(WSAEVENT) * (context->max_fds + 1));
+	if (context->events == NULL) {
+		lwsl_err("Unable to allocate events array for %d connections\n",
+			context->max_fds);
+		return 1;
+	}
+
+	context->fds_count = 0;
+	context->events[0] = WSACreateEvent();
+
+	context->fd_random = 0;
+
+	context->fops.open	= _lws_plat_file_open;
+	context->fops.close	= _lws_plat_file_close;
+	context->fops.seek_cur	= _lws_plat_file_seek_cur;
+	context->fops.read	= _lws_plat_file_read;
+	context->fops.write	= _lws_plat_file_write;
+
+	return 0;
+}
diff --git a/lib/output.c b/lib/output.c
index 9985e792..58d86eae 100644
--- a/lib/output.c
+++ b/lib/output.c
@@ -509,8 +509,8 @@ send_raw:
 LWS_VISIBLE int lws_serve_http_file_fragment(struct lws_context *context,
 					     struct lws *wsi)
 {
-	int n;
-	int m;
+	unsigned long amount;
+	int n, m;
 
 	while (!lws_send_pipe_choked(wsi)) {
 
@@ -518,7 +518,7 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws_context *context,
 			if (lws_issue_raw(wsi, wsi->truncated_send_malloc +
 					  wsi->truncated_send_offset,
 					  wsi->truncated_send_len) < 0) {
-				lwsl_info("closing from lws_serve_http_file_fragment\n");
+				lwsl_info("%s: closing\n", __func__);
 				return -1;
 			}
 			continue;
@@ -527,10 +527,12 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws_context *context,
 		if (wsi->u.http.filepos == wsi->u.http.filelen)
 			goto all_sent;
 
-		compatible_file_read(n, wsi->u.http.fd, context->service_buffer,
-				     sizeof(context->service_buffer));
-		if (n < 0)
+		if (lws_plat_file_read(&context->fops, wsi->u.http.fd, &amount,
+				       context->service_buffer,
+				       sizeof(context->service_buffer)) < 0)
 			return -1; /* caller will close */
+
+		n = (int)amount;
 		if (n) {
 			lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
 					AWAITING_TIMEOUT);
@@ -543,7 +545,9 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws_context *context,
 
 			if (m != n)
 				/* adjust for what was not sent */
-				if (compatible_file_seek_cur(wsi->u.http.fd, m - n) < 0)
+				if (lws_plat_file_seek_cur(&context->fops,
+							   wsi->u.http.fd,
+							   m - n) < 0)
 					return -1;
 		}
 all_sent:
@@ -552,7 +556,7 @@ all_sent:
 			wsi->state = WSI_STATE_HTTP;
 
 			/* we might be in keepalive, so close it off here */
-			compatible_file_close(wsi->u.http.fd);
+			lws_plat_file_close(&context->fops, wsi->u.http.fd);
 			wsi->u.http.fd = LWS_INVALID_FILE;
 
 			if (wsi->protocol->callback)
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 99e34ba3..e406b2fa 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -62,16 +62,6 @@
 #define SOL_TCP IPPROTO_TCP
 
 #define compatible_close(fd) closesocket(fd)
-#define compatible_file_close(fd) CloseHandle(fd)
-#define compatible_file_seek_cur(fd, offset) \
-	SetFilePointer(fd, offset, NULL, FILE_CURRENT)
-#define compatible_file_read(amount, fd, buf, len) {\
-	DWORD _amount; \
-	if (!ReadFile(fd, buf, len, &_amount, NULL)) \
-		amount = -1; \
-	else \
-		amount = _amount; \
-	}
 #define lws_set_blocking_send(wsi) wsi->sock_send_blocking = TRUE
 #define lws_socket_is_valid(x) (!!x)
 #define LWS_SOCK_INVALID 0 
@@ -91,7 +81,6 @@
 #define vsnprintf _vsnprintf
 #endif
 
-#define LWS_INVALID_FILE INVALID_HANDLE_VALUE
 #else /* not windows --> */
 
 #include <fcntl.h>
@@ -144,15 +133,10 @@
 #define LWS_EINTR EINTR
 #define LWS_EISCONN EISCONN
 #define LWS_EWOULDBLOCK EWOULDBLOCK
-#define LWS_INVALID_FILE -1
 #define LWS_POLLHUP (POLLHUP|POLLERR)
 #define LWS_POLLIN (POLLIN)
 #define LWS_POLLOUT (POLLOUT)
 #define compatible_close(fd) close(fd)
-#define compatible_file_close(fd) close(fd)
-#define compatible_file_seek_cur(fd, offset) lseek(fd, offset, SEEK_CUR)
-#define compatible_file_read(amount, fd, buf, len) \
-		amount = read(fd, buf, len);
 #define lws_set_blocking_send(wsi)
 
 #ifdef MBED_OPERATORS
@@ -546,8 +530,10 @@ struct lws_context {
 #ifndef LWS_NO_EXTENSIONS
 	struct lws_extension *extensions;
 #endif
-    struct lws_token_limits *token_limits;
+	struct lws_token_limits *token_limits;
 	void *user_space;
+	
+	struct lws_plat_file_ops fops;
 };
 
 enum {
@@ -644,11 +630,7 @@ struct allocated_headers {
 struct _lws_http_mode_related {
 	/* MUST be first in struct */
 	struct allocated_headers *ah; /* mirroring  _lws_header_related */
-#if defined(WIN32) || defined(_WIN32)
-	HANDLE fd;
-#else
-	int fd;
-#endif
+	lws_filefd_type fd;
 	unsigned long filepos;
 	unsigned long filelen;
 
@@ -1142,12 +1124,6 @@ interface_to_sa(struct lws_context *context, const char *ifname,
 #endif
 LWS_EXTERN void lwsl_emit_stderr(int level, const char *line);
 
-#ifdef _WIN32
-LWS_EXTERN HANDLE lws_plat_open_file(const char* filename, unsigned long* filelen);
-#else
-LWS_EXTERN int lws_plat_open_file(const char* filename, unsigned long* filelen);
-#endif
-
 enum lws_ssl_capable_status {
 	LWS_SSL_CAPABLE_ERROR = -1,
 	LWS_SSL_CAPABLE_MORE_SERVICE = -2,
diff --git a/lib/server.c b/lib/server.c
index 2473cb5a..08444354 100644
--- a/lib/server.c
+++ b/lib/server.c
@@ -914,7 +914,8 @@ LWS_VISIBLE int lws_serve_http_file(struct lws_context *context,
 			     LWS_SEND_BUFFER_PRE_PADDING;
 	int ret = 0;
 
-	wsi->u.http.fd = lws_plat_open_file(file, &wsi->u.http.filelen);
+	wsi->u.http.fd = lws_plat_file_open(&context->fops, file,
+					    &wsi->u.http.filelen, O_RDONLY);
 
 	if (wsi->u.http.fd == LWS_INVALID_FILE) {
 		lwsl_err("Unable to open '%s'\n", file);
@@ -948,8 +949,7 @@ LWS_VISIBLE int lws_serve_http_file(struct lws_context *context,
 	if (lws_finalize_http_header(context, wsi, &p, end))
 		return -1;
 	
-	ret = lws_write(wsi, response,
-				   p - response, LWS_WRITE_HTTP_HEADERS);
+	ret = lws_write(wsi, response, p - response, LWS_WRITE_HTTP_HEADERS);
 	if (ret != (p - response)) {
 		lwsl_err("_write returned %d from %d\n", ret, (p - response));
 		return -1;
diff --git a/test-server/test-server-http.c b/test-server/test-server-http.c
index 7456d01a..4e05b521 100644
--- a/test-server/test-server-http.c
+++ b/test-server/test-server-http.c
@@ -117,7 +117,7 @@ int callback_http(struct lws_context *context, struct lws *wsi,
 	struct per_session_data__http *pss =
 			(struct per_session_data__http *)user;
 	static unsigned char buffer[4096];
-	struct stat stat_buf;
+	unsigned long amount, file_len;
 	char leaf_path[1024];
 	const char *mimetype;
 	char *other_headers;
@@ -165,16 +165,12 @@ int callback_http(struct lws_context *context, struct lws *wsi,
 
 			p = buffer + LWS_SEND_BUFFER_PRE_PADDING;
 			end = p + sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
-#ifdef _WIN32
-			pss->fd = open(leaf_path, O_RDONLY | _O_BINARY);
-#else
-			pss->fd = open(leaf_path, O_RDONLY);
-#endif
 
-			if (pss->fd < 0)
-				return -1;
+			pss->fd = lws_plat_file_open(lws_get_fops(context),
+						     leaf_path, &file_len,
+						     O_RDONLY);
 
-			if (fstat(pss->fd, &stat_buf) < 0)
+			if (pss->fd == LWS_INVALID_FILE)
 				return -1;
 
 			/*
@@ -200,7 +196,8 @@ int callback_http(struct lws_context *context, struct lws *wsi,
 					10, &p, end))
 				return 1;
 			if (lws_add_http_header_content_length(context, wsi,
-						stat_buf.st_size, &p, end))
+							       file_len, &p,
+							       end))
 				return 1;
 			if (lws_finalize_http_header(context, wsi, &p, end))
 				return 1;
@@ -216,13 +213,13 @@ int callback_http(struct lws_context *context, struct lws *wsi,
 			 * this is mandated by changes in HTTP2
 			 */
 
-			n = lws_write(wsi,
-					buffer + LWS_SEND_BUFFER_PRE_PADDING,
-					p - (buffer + LWS_SEND_BUFFER_PRE_PADDING),
-					LWS_WRITE_HTTP_HEADERS);
+			n = lws_write(wsi, buffer + LWS_SEND_BUFFER_PRE_PADDING,
+				      p - (buffer + LWS_SEND_BUFFER_PRE_PADDING),
+				      LWS_WRITE_HTTP_HEADERS);
 
 			if (n < 0) {
-				close(pss->fd);
+				lws_plat_file_close(lws_get_fops(context),
+						    pss->fd);
 				return -1;
 			}
 			/*
@@ -328,11 +325,13 @@ int callback_http(struct lws_context *context, struct lws *wsi,
 				/* he couldn't handle that much */
 				n = m;
 			
-			n = read(pss->fd, buffer + LWS_SEND_BUFFER_PRE_PADDING,
-				 n);
+			n = lws_plat_file_read(lws_get_fops(context), pss->fd,
+					       &amount, buffer +
+					        LWS_SEND_BUFFER_PRE_PADDING, n);
 			/* problem reading, close conn */
 			if (n < 0)
 				goto bail;
+			n = (int)amount;
 			/* sent it all, close conn */
 			if (n == 0)
 				goto flush_bail;
@@ -354,7 +353,8 @@ int callback_http(struct lws_context *context, struct lws *wsi,
 			 */
 			if (m != n)
 				/* partial write, adjust */
-				if (lseek(pss->fd, m - n, SEEK_CUR) < 0)
+				if (lws_plat_file_seek_cur(lws_get_fops(context),
+							   pss->fd, m - n) < 0)
 					goto bail;
 
 			if (m) /* while still active, extend timeout */
@@ -377,11 +377,11 @@ flush_bail:
 			lws_callback_on_writable(context, wsi);
 			break;
 		}
-		close(pss->fd);
+		lws_plat_file_close(lws_get_fops(context), pss->fd);
 		goto try_to_reuse;
 
 bail:
-		close(pss->fd);
+		lws_plat_file_close(lws_get_fops(context), pss->fd);
 		return -1;
 
 	/*
diff --git a/test-server/test-server.c b/test-server/test-server.c
index 2ffe0625..5620e4e7 100644
--- a/test-server/test-server.c
+++ b/test-server/test-server.c
@@ -31,6 +31,7 @@ int count_pollfds;
 #endif
 volatile int force_exit = 0;
 struct lws_context *context;
+struct lws_plat_file_ops fops_plat;
 
 /* http server gets files from this path */
 #define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
@@ -97,6 +98,25 @@ static struct lws_protocols protocols[] = {
 	{ NULL, NULL, 0, 0 } /* terminator */
 };
 
+
+/* this shows how to override the lws file operations.  You don't need
+ * to do any of this unless you have a reason (eg, want to serve
+ * compressed files without decompressing the whole archive)
+ */
+static lws_filefd_type
+test_server_fops_open(const char *filename, unsigned long *filelen, int flags)
+{
+	int n;
+
+	/* call through to original platform implementation */
+	n = fops_plat.open(filename, filelen, flags);
+
+	lwsl_notice("%s: opening %s, ret %d, len %lu\n", __func__, filename,
+		    n, *filelen);
+
+	return n;
+}
+
 void sighandler(int sig)
 {
 	force_exit = 1;
@@ -271,6 +291,15 @@ int main(int argc, char **argv)
 		return -1;
 	}
 
+	/* this shows how to override the lws file operations.  You don't need
+	 * to do any of this unless you have a reason (eg, want to serve
+	 * compressed files without decompressing the whole archive)
+	 */
+	/* stash original platform fops */
+	fops_plat = *(lws_get_fops(context));
+	/* override the active fops */
+	lws_get_fops(context)->open = test_server_fops_open;
+
 	n = 0;
 	while (n >= 0 && !force_exit) {
 		struct timeval tv;
diff --git a/test-server/test-server.h b/test-server/test-server.h
index 3ebf6f79..457f6aa5 100644
--- a/test-server/test-server.h
+++ b/test-server/test-server.h
@@ -43,7 +43,7 @@ extern void test_server_unlock(int care);
 #endif
 
 struct per_session_data__http {
-	int fd;
+	lws_filefd_type fd;
 };
 
 /*
@@ -63,18 +63,18 @@ struct per_session_data__lws_mirror {
 	int ringbuffer_tail;
 };
 
-extern int callback_http(struct lws_context *context,
-			 struct lws *wsi,
-			 enum lws_callback_reasons reason,
-			 void *user, void *in, size_t len);
-extern int callback_lws_mirror(struct lws_context *context,
-			       struct lws *wsi,
-			       enum lws_callback_reasons reason,
-			       void *user, void *in, size_t len);
-extern int callback_dumb_increment(struct lws_context *context,
-			       struct lws *wsi,
-			       enum lws_callback_reasons reason,
-			       void *user, void *in, size_t len);
+extern int
+callback_http(struct lws_context *context, struct lws *wsi,
+	      enum lws_callback_reasons reason, void *user, void *in,
+	      size_t len);
+extern int
+callback_lws_mirror(struct lws_context *context, struct lws *wsi,
+		    enum lws_callback_reasons reason, void *user, void *in,
+		    size_t len);
+extern int
+callback_dumb_increment(struct lws_context *context, struct lws *wsi,
+			enum lws_callback_reasons reason, void *user, void *in,
+			size_t len);
 
 extern void
 dump_handshake_info(struct lws *wsi);
-- 
GitLab