diff --git a/lib/context.c b/lib/context.c
index 1a418a622..ef0362e5a 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -174,6 +174,8 @@ libwebsocket_create_context(struct lws_context_creation_info *info)
 		goto bail;
 	}
 
+	lws_context_init_file_callbacks(info, context);
+
 	lws_context_init_extensions(info, context);
 
 	context->user_space = info->user;
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index cb4a9efd6..f378f0534 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -118,7 +118,7 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context,
 		if (wsi->u.http.fd != LWS_INVALID_FILE) {
 			// TODO: If we're just closing with LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY this file descriptor might leak?
 			lwsl_debug("closing http file\n");
-			compatible_file_close(wsi->u.http.fd);
+			context->file_callbacks.pfn_close(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);
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index c1285e860..961199fc3 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -1119,6 +1119,24 @@ struct libwebsocket_extension {
 };
 #endif
 
+/**
+ * struct libwebsocket_file_callbacks -	File operations callbacks
+ *
+ * @pfn_open:		Open file (always binary access)
+ * @pfn_close:		Close file
+ * @pfn_seek_cur:	Seeking function from current position
+ * @pfn_read:		File read function
+ *				'amount' is a count of bytes read,
+ *				'len' is count of bytes to read
+ */
+
+struct libwebsocket_file_callbacks {
+	void* (*pfn_open)(const char* filename, unsigned long* filelen);
+	void (*pfn_close)(void* handle);
+	unsigned long (*pfn_seek_cur)(void* handle, long offsetFromCurPos);
+	void (*pfn_read)(unsigned long* amount, void* handle, unsigned char* buf, unsigned long len);
+};
+
 /**
  * struct lws_context_creation_info: parameters to create context with
  *
@@ -1135,6 +1153,8 @@ struct libwebsocket_extension {
  * @extensions: NULL or array of libwebsocket_extension structs listing the
  *		extensions this context supports.  If you configured with
  *		--without-extensions, you should give NULL here.
+ * @file_callbacks: custom file operation callbacks 
+		to support virtual file system defined by application
  * @token_limits: NULL or struct lws_token_limits pointer which is initialized
  *      with a token length limit for each possible WSI_TOKEN_*** 
  * @ssl_cert_filepath:	If libwebsockets was compiled to use ssl, and you want
@@ -1175,6 +1195,7 @@ struct lws_context_creation_info {
 	const char *iface;
 	struct libwebsocket_protocols *protocols;
 	struct libwebsocket_extension *extensions;
+	struct libwebsocket_file_callbacks *file_callbacks;
 	struct lws_token_limits *token_limits;
 	const char *ssl_private_key_password;
 	const char *ssl_cert_filepath;
diff --git a/lib/lws-plat-win.c b/lib/lws-plat-win.c
index 01e8a058d..3fcfb1ebf 100644
--- a/lib/lws-plat-win.c
+++ b/lib/lws-plat-win.c
@@ -395,23 +395,6 @@ lws_plat_change_pollfd(struct libwebsocket_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)
 { 
diff --git a/lib/output.c b/lib/output.c
index 6a595c9c0..9628d35d3 100644
--- a/lib/output.c
+++ b/lib/output.c
@@ -536,7 +536,7 @@ LWS_VISIBLE int libwebsockets_serve_http_file_fragment(
 		if (wsi->u.http.filepos == wsi->u.http.filelen)
 			goto all_sent;
 
-		compatible_file_read(n, wsi->u.http.fd, context->service_buffer,
+		context->file_callbacks.pfn_read(&n, wsi->u.http.fd, context->service_buffer,
 					       sizeof(context->service_buffer));
 		if (n < 0)
 			return -1; /* caller will close */
@@ -550,7 +550,7 @@ LWS_VISIBLE int libwebsockets_serve_http_file_fragment(
 
 			if (m != n)
 				/* adjust for what was not sent */
-				if (compatible_file_seek_cur(wsi->u.http.fd, m - n) < 0)
+				if (context->file_callbacks.pfn_seek_cur(wsi->u.http.fd, m - n) < 0)
 					return -1;
 		}
 all_sent:
@@ -559,7 +559,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);
+			context->file_callbacks.pfn_close(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 5ec133d9d..0d2ca2ba8 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -59,17 +59,8 @@
 #define MSG_NOSIGNAL 0
 #define SHUT_RDWR SD_BOTH
 #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 
@@ -147,10 +138,7 @@
 #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
@@ -544,6 +532,7 @@ struct libwebsocket_context {
 #ifndef LWS_NO_EXTENSIONS
 	struct libwebsocket_extension *extensions;
 #endif
+	struct libwebsocket_file_callbacks file_callbacks;
     struct lws_token_limits *token_limits;
 	void *user_space;
 };
@@ -1031,6 +1020,10 @@ lws_ext_callback_for_each_extension_type(
 #define lws_context_init_extensions(_a, _b)
 #endif
 
+LWS_EXTERN void
+lws_context_init_file_callbacks(struct lws_context_creation_info *info, 
+		struct libwebsocket_context *context);
+
 LWS_EXTERN int
 lws_client_interpret_server_handshake(struct libwebsocket_context *context,
 		struct libwebsocket *wsi);
@@ -1149,12 +1142,6 @@ LWS_EXTERN int interface_to_sa(struct libwebsocket_context *context,
 #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 4256d3c54..d5d23bd49 100644
--- a/lib/server.c
+++ b/lib/server.c
@@ -921,7 +921,7 @@ LWS_VISIBLE int libwebsockets_serve_http_file(
 					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 = context->file_callbacks.pfn_open(file, &wsi->u.http.filelen);
 
 	if (wsi->u.http.fd == LWS_INVALID_FILE) {
 		lwsl_err("Unable to open '%s'\n", file);
diff --git a/test-server/test-server.c b/test-server/test-server.c
index 8949dfd25..c53037e9c 100644
--- a/test-server/test-server.c
+++ b/test-server/test-server.c
@@ -287,6 +287,8 @@ int main(int argc, char **argv)
 		 * which also includes libwebsocket sockets
 		 */
 
+		pollfds->events = (0x0100 | 0x0200);
+
 		n = poll(pollfds, count_pollfds, 50);
 		if (n < 0)
 			continue;