From eeb76e822a9cc2a43764678ccdb72f35c816c8be Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sat, 5 May 2018 06:02:44 +0800 Subject: [PATCH] minimal-http-server-mimetypes --- minimal-examples/http-server/README.md | 1 + .../CMakeLists.txt | 79 +++++++++++++++ .../minimal-http-server-mimetypes/README.md | 21 ++++ .../minimal-http-server-mimetypes.c | 92 ++++++++++++++++++ .../mount-origin/404.html | 9 ++ .../mount-origin/favicon.ico | Bin 0 -> 1406 bytes .../mount-origin/index.html | 20 ++++ .../mount-origin/libwebsockets.org-logo.png | Bin 0 -> 7029 bytes .../mount-origin/test.tar.bz2 | Bin 0 -> 7589 bytes 9 files changed, 222 insertions(+) create mode 100644 minimal-examples/http-server/minimal-http-server-mimetypes/CMakeLists.txt create mode 100644 minimal-examples/http-server/minimal-http-server-mimetypes/README.md create mode 100644 minimal-examples/http-server/minimal-http-server-mimetypes/minimal-http-server-mimetypes.c create mode 100644 minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/404.html create mode 100644 minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/favicon.ico create mode 100644 minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/index.html create mode 100644 minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/libwebsockets.org-logo.png create mode 100644 minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/test.tar.bz2 diff --git a/minimal-examples/http-server/README.md b/minimal-examples/http-server/README.md index 1adb6943..db5579b2 100644 --- a/minimal-examples/http-server/README.md +++ b/minimal-examples/http-server/README.md @@ -8,6 +8,7 @@ minimal-http-server-eventlib|Same as minimal-http-server but works with a suppor minimal-http-server-form-get|Process a GET form minimal-http-server-form-post-file|Process a multipart POST form with file transfer minimal-http-server-form-post|Process a POST form (no file transfer) +minimal-http-server-mimetypes|Shows how to add support for additional mimetypes at runtime minimal-http-server-multivhost|Same as minimal-http-server but three different vhosts minimal-http-server-smp|Multiple service threads minimal-http-server-sse-ring|Server Side Events with ringbuffer and threaded event sources diff --git a/minimal-examples/http-server/minimal-http-server-mimetypes/CMakeLists.txt b/minimal-examples/http-server/minimal-http-server-mimetypes/CMakeLists.txt new file mode 100644 index 00000000..d270d7b5 --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-mimetypes/CMakeLists.txt @@ -0,0 +1,79 @@ +cmake_minimum_required(VERSION 2.8) +include(CheckCSourceCompiles) + +set(SAMP lws-minimal-http-server-mimetypes) +set(SRCS minimal-http-server-mimetypes.c) + +# If we are being built as part of lws, confirm current build config supports +# reqconfig, else skip building ourselves. +# +# If we are being built externally, confirm installed lws was configured to +# support reqconfig, else error out with a helpful message about the problem. +# +MACRO(require_lws_config reqconfig _val result) + + if (DEFINED ${reqconfig}) + if (${reqconfig}) + set (rq 1) + else() + set (rq 0) + endif() + else() + set(rq 0) + endif() + + if (${_val} EQUAL ${rq}) + set(SAME 1) + else() + set(SAME 0) + endif() + + if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) + if (${_val}) + message("${SAMP}: skipping as lws being built without ${reqconfig}") + else() + message("${SAMP}: skipping as lws built with ${reqconfig}") + endif() + set(${result} 0) + else() + if (LWS_WITH_MINIMAL_EXAMPLES) + set(MET ${SAME}) + else() + CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) + if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) + set(HAS_${reqconfig} 0) + else() + set(HAS_${reqconfig} 1) + endif() + if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) + set(MET 1) + else() + set(MET 0) + endif() + endif() + if (NOT MET) + if (${_val}) + message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") + else() + message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") + endif() + endif() + + endif() +ENDMACRO() + + +set(requirements 1) +require_lws_config(LWS_ROLE_H1 1 requirements) +require_lws_config(LWS_WITHOUT_SERVER 0 requirements) + +if (requirements) + add_executable(${SAMP} ${SRCS}) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets) + endif() +endif() diff --git a/minimal-examples/http-server/minimal-http-server-mimetypes/README.md b/minimal-examples/http-server/minimal-http-server-mimetypes/README.md new file mode 100644 index 00000000..3240d30a --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-mimetypes/README.md @@ -0,0 +1,21 @@ +# lws minimal http server mimetypes + +This is the same as the basic minimal http server, but it demonstrates how to +add support for extra mimetypes to a mount. + +## build + +``` + $ cmake . && make +``` + +## usage + +``` + $ ./lws-minimal-http-server +[2018/03/04 09:30:02:7986] USER: LWS minimal http server | visit http://localhost:7681 +[2018/03/04 09:30:02:7986] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 on +``` + +Visit http://localhost:7681 and click on the link to download the test.tar.bz2 file. + diff --git a/minimal-examples/http-server/minimal-http-server-mimetypes/minimal-http-server-mimetypes.c b/minimal-examples/http-server/minimal-http-server-mimetypes/minimal-http-server-mimetypes.c new file mode 100644 index 00000000..e97a5661 --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-mimetypes/minimal-http-server-mimetypes.c @@ -0,0 +1,92 @@ +/* + * lws-minimal-http-server + * + * Copyright (C) 2018 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This demonstrates the most minimal http server you can make with lws. + * + * To keep it simple, it serves stuff from the subdirectory + * "./mount-origin" of the directory it was started in. + * You can change that by changing mount.origin below. + */ + +#include +#include +#include + +static int interrupted; + +static const struct lws_protocol_vhost_options pvo_mime = { + NULL, /* "next" pvo linked-list */ + NULL, /* "child" pvo linked-list */ + ".bz2", /* file suffix to match */ + "application/x-bzip2" /* mimetype to use */ +}; + +static const struct lws_http_mount mount = { + /* .mount_next */ NULL, /* linked-list "next" */ + /* .mountpoint */ "/", /* mountpoint URL */ + /* .origin */ "./mount-origin", /* serve from dir */ + /* .def */ "index.html", /* default filename */ + /* .protocol */ NULL, + /* .cgienv */ NULL, + /* .extra_mimetypes */ &pvo_mime, + /* .interpret */ NULL, + /* .cgi_timeout */ 0, + /* .cache_max_age */ 0, + /* .auth_mask */ 0, + /* .cache_reusable */ 0, + /* .cache_revalidate */ 0, + /* .cache_intermediaries */ 0, + /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ + /* .mountpoint_len */ 1, /* char count */ + /* .basic_auth_login_file */ NULL, +}; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, const char **argv) +{ + struct lws_context_creation_info info; + struct lws_context *context; + const char *p; + int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE + /* for LLL_ verbosity above NOTICE to be built into lws, + * lws must have been configured and built with + * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ + /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ + /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ + /* | LLL_DEBUG */; + + signal(SIGINT, sigint_handler); + + if ((p = lws_cmdline_option(argc, argv, "-d"))) + logs = atoi(p); + + lws_set_log_level(logs, NULL); + lwsl_user("LWS minimal http server | visit http://localhost:7681\n"); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.port = 7681; + info.mounts = &mount; + info.error_document_404 = "/404.html"; + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + while (n >= 0 && !interrupted) + n = lws_service(context, 1000); + + lws_context_destroy(context); + + return 0; +} diff --git a/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/404.html b/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/404.html new file mode 100644 index 00000000..1f7ae66e --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/404.html @@ -0,0 +1,9 @@ + + + +
+

404

+ Sorry, that file doesn't exist. + + + diff --git a/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/favicon.ico b/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c0cc2e3dff34012ba3d4a7848a7ed17579788ec5 GIT binary patch literal 1406 zcmZQzU<5(|0R}M0U}azs1F|%L7$l?s#Ec9aKoZP=&`9i!<^REA8>%80(yxAC$j<-A zkb5S8;qL6446ipNFl>5#fuVR6L=8goC~GtXMnhmYga9MSfQgBTk&TUw5$JocUP63y z3phA97+G0a8QIy{!BT|y==xb$SQt4uIT@LmnZZ(o_~`mk`Tv1M8w?+DXJCL~kQj^& JqOtKoVgQl$ETjMc literal 0 HcmV?d00001 diff --git a/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/index.html b/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/index.html new file mode 100644 index 00000000..8201c5fa --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/index.html @@ -0,0 +1,20 @@ + + + +
+ + Hello from the minimal http server + mimetypes example. +
+ This shows how to teach a mount new bindings between file
+ suffix and mimetype used to serve it.

+ + Lws has a bunch of built-in ones, but you can add as many
+ as you like when defining the mount.

+ + For example, lws doesn't know the suffix [.tar].bz2
+ implies the mimetype application/x-bzip2, but we taught
+ this mount about that relationship in the example code, so it
+ knows how to serve this example test.tar.bz2. + + + diff --git a/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/libwebsockets.org-logo.png b/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/libwebsockets.org-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2060a10c936a0959f2a5c3a6b7fa60ac324f1a95 GIT binary patch literal 7029 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 H_0GV+DB