mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
minimal: dynamic http server
This commit is contained in:
parent
2519ac9ced
commit
5ecec970b2
11 changed files with 389 additions and 2 deletions
36
lib/header.c
36
lib/header.c
|
@ -79,6 +79,25 @@ int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_finalize_write_http_header(struct lws *wsi, unsigned char *start,
|
||||
unsigned char **pp, unsigned char *end)
|
||||
{
|
||||
unsigned char *p;
|
||||
int len;
|
||||
|
||||
if (lws_finalize_http_header(wsi, pp, end))
|
||||
return 1;
|
||||
|
||||
p = *pp;
|
||||
len = lws_ptr_diff(p, start);
|
||||
|
||||
if (lws_write(wsi, start, len, LWS_WRITE_HTTP_HEADERS) != len)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
|
||||
const unsigned char *value, int length,
|
||||
|
@ -117,6 +136,23 @@ int lws_add_http_header_content_length(struct lws *wsi,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_add_http_common_headers(struct lws *wsi, unsigned int code,
|
||||
const char *content_type, lws_filepos_t content_len,
|
||||
unsigned char **p, unsigned char *end)
|
||||
{
|
||||
if (lws_add_http_header_status(wsi, code, p, end))
|
||||
return 1;
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
|
||||
(unsigned char *)content_type,
|
||||
strlen(content_type), p, end))
|
||||
return 1;
|
||||
if (lws_add_http_header_content_length(wsi, content_len, p, end))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
STORE_IN_ROM static const char * const err400[] = {
|
||||
"Bad Request",
|
||||
"Unauthorized",
|
||||
|
|
|
@ -4131,6 +4131,49 @@ lws_add_http_header_content_length(struct lws *wsi,
|
|||
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_finalize_http_header(struct lws *wsi, unsigned char **p,
|
||||
unsigned char *end);
|
||||
|
||||
/**
|
||||
* lws_finalize_write_http_header() - Helper finializing and writing http headers
|
||||
*
|
||||
* \param wsi: the connection to check
|
||||
* \param start: pointer to the start of headers in the buffer, eg &buf[LWS_PRE]
|
||||
* \param p: pointer to current position in buffer pointer
|
||||
* \param end: pointer to end of buffer
|
||||
*
|
||||
* Terminates the headers correctly accoring to the protocol in use (h1 / h2)
|
||||
* and writes the headers. Returns nonzero for error.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_finalize_write_http_header(struct lws *wsi, unsigned char *start,
|
||||
unsigned char **p, unsigned char *end);
|
||||
|
||||
/**
|
||||
* lws_add_http_common_headers() - Helper preparing common http headers
|
||||
*
|
||||
* \param wsi: the connection to check
|
||||
* \param code: an HTTP code like 200, 404 etc (see enum http_status)
|
||||
* \param content_type: the content type, like "text/html"
|
||||
* \param content_len: the content length, in bytes
|
||||
* \param p: pointer to current position in buffer pointer
|
||||
* \param end: pointer to end of buffer
|
||||
*
|
||||
* Adds the initial response code, so should be called first.
|
||||
*
|
||||
* Code may additionally take OR'd flags:
|
||||
*
|
||||
* LWSAHH_FLAG_NO_SERVER_NAME: don't apply server name header this time
|
||||
*
|
||||
* This helper just calls public apis to simplify adding headers that are
|
||||
* commonly needed. If it doesn't fit your case, or you want to add additional
|
||||
* headers just call the public apis directly yourself for what you want.
|
||||
*
|
||||
* It does not call lws_finalize_http_header(), to allow you to add further
|
||||
* headers after calling this. You will need to call that yourself at the end.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_add_http_common_headers(struct lws *wsi, unsigned int code,
|
||||
const char *content_type, lws_filepos_t content_len,
|
||||
unsigned char **p, unsigned char *end);
|
||||
///@}
|
||||
|
||||
/** \defgroup form-parsing Form Parsing
|
||||
|
|
|
@ -1474,7 +1474,9 @@ lws_http_action(struct lws *wsi)
|
|||
wsi->cache_revalidate = hit->cache_revalidate;
|
||||
wsi->cache_intermediaries = hit->cache_intermediaries;
|
||||
|
||||
n = lws_http_serve(wsi, s, hit->origin, hit);
|
||||
n = 1;
|
||||
if (hit->origin_protocol == LWSMPRO_FILE)
|
||||
n = lws_http_serve(wsi, s, hit->origin, hit);
|
||||
if (n) {
|
||||
/*
|
||||
* lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
|
||||
|
@ -2795,7 +2797,7 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
|
|||
wsi->http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
|
||||
file, vpath, &fflags);
|
||||
if (!wsi->http.fop_fd) {
|
||||
lwsl_err("Unable to open '%s': errno %d\n", file, errno);
|
||||
lwsl_err("Unable to open: '%s': errno %d\n", file, errno);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -4,3 +4,4 @@ minimal-http-server|Serves a directory over http/1 or http/2, custom 404 handler
|
|||
minimal-http-server-libuv|Same as minimal-http-server but libuv event loop
|
||||
minimal-http-server-multivhost|Same as minimal-http-server but three different vhosts
|
||||
minimal-http-server-smp|Multiple service threads
|
||||
minimal-http-server-dynamic|Serves both static and dynamically generated http content
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
include(CheckSymbolExists)
|
||||
|
||||
set(SAMP lws-minimal-http-server-dynamic)
|
||||
set(SRCS minimal-http-server-dynamic.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_SYMBOL_EXISTS(${reqconfig} libwebsockets.h HAS)
|
||||
if (NOT DEFINED HAS)
|
||||
set(HAS 0)
|
||||
endif()
|
||||
if ((HAS AND ${_val}) OR (NOT HAS 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_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()
|
|
@ -0,0 +1,20 @@
|
|||
# lws minimal http server dynamic content
|
||||
|
||||
## build
|
||||
|
||||
```
|
||||
$ cmake . && make
|
||||
```
|
||||
|
||||
## usage
|
||||
|
||||
```
|
||||
$ ./lws-minimal-http-server-dynamic
|
||||
[2018/03/20 10:24:24:7099] USER: LWS minimal http server dynamic | visit http://localhost:7681
|
||||
[2018/03/20 10:24:24:7099] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off
|
||||
```
|
||||
|
||||
Visit http://localhost:7681, which is all static content.
|
||||
|
||||
Click on the link to /dyn/anything, this opens a new tab with dynamicly-produced content.
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* lws-minimal-http-server-dynamic
|
||||
*
|
||||
* Copyright (C) 2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
* This demonstrates a minimal http server that can produce dynamic http
|
||||
* content as well as static content.
|
||||
*
|
||||
* To keep it simple, it serves the static stuff from the subdirectory
|
||||
* "./mount-origin" of the directory it was started in.
|
||||
*
|
||||
* You can change that by changing mount.origin below.
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
|
||||
/*
|
||||
* Unlike ws, http is a stateless protocol. This pss only exists for the
|
||||
* duration of a single http transaction. With http/1.1 keep-alive and http/2,
|
||||
* that is unrelated to (shorter than) the lifetime of the network connection.
|
||||
*/
|
||||
struct pss {
|
||||
char str[128];
|
||||
int len;
|
||||
};
|
||||
|
||||
static int interrupted;
|
||||
|
||||
static int
|
||||
callback_dynamic_http(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
struct pss *pss = (struct pss *)user;
|
||||
uint8_t buf[LWS_PRE + 256], *start = &buf[LWS_PRE], *p = start,
|
||||
*end = &buf[sizeof(buf) - 1];
|
||||
time_t t;
|
||||
|
||||
switch (reason) {
|
||||
case LWS_CALLBACK_HTTP:
|
||||
|
||||
/* in contains the url part after our mountpoint /dyn, if any */
|
||||
|
||||
t = time(NULL);
|
||||
pss->len = lws_snprintf(pss->str, sizeof(pss->str),
|
||||
"<html>"
|
||||
"<img src=\"/libwebsockets.org-logo.png\">"
|
||||
"<br>Dynamic content for '%s' from mountpoint."
|
||||
"<br>Time: %s"
|
||||
"</html>", (const char *)in, ctime(&t));
|
||||
|
||||
/* prepare and write http headers */
|
||||
if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK,
|
||||
"text/html", pss->len, &p, end))
|
||||
return 1;
|
||||
if (lws_finalize_write_http_header(wsi, start, &p, end))
|
||||
return 1;
|
||||
|
||||
/* write the body separately */
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
return 0;
|
||||
|
||||
case LWS_CALLBACK_HTTP_WRITEABLE:
|
||||
|
||||
if (!pss || !pss->len)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Use LWS_WRITE_HTTP for intermediate writes, on http/2
|
||||
* lws uses this to understand to end the stream with this
|
||||
* frame
|
||||
*/
|
||||
if (lws_write(wsi, (uint8_t *)pss->str, pss->len,
|
||||
LWS_WRITE_HTTP_FINAL) != pss->len)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* HTTP/1.0 no keepalive: close network connection
|
||||
* HTTP/1.1 or HTTP1.0 + KA: wait / process next transaction
|
||||
* HTTP/2: stream ended, parent connection remains up
|
||||
*/
|
||||
if (lws_http_transaction_completed(wsi))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return lws_callback_http_dummy(wsi, reason, user, in, len);
|
||||
}
|
||||
|
||||
static struct lws_protocols protocols[] = {
|
||||
{ "http", callback_dynamic_http, sizeof(struct pss), 0 },
|
||||
{ NULL, NULL, 0, 0 } /* terminator */
|
||||
};
|
||||
|
||||
/* override the default mount for /dyn in the URL space */
|
||||
|
||||
static const struct lws_http_mount mount_dyn = {
|
||||
/* .mount_next */ NULL, /* linked-list "next" */
|
||||
/* .mountpoint */ "/dyn", /* mountpoint URL */
|
||||
/* .origin */ NULL, /* protocol */
|
||||
/* .def */ NULL,
|
||||
/* .protocol */ "http",
|
||||
/* .cgienv */ NULL,
|
||||
/* .extra_mimetypes */ NULL,
|
||||
/* .interpret */ NULL,
|
||||
/* .cgi_timeout */ 0,
|
||||
/* .cache_max_age */ 0,
|
||||
/* .auth_mask */ 0,
|
||||
/* .cache_reusable */ 0,
|
||||
/* .cache_revalidate */ 0,
|
||||
/* .cache_intermediaries */ 0,
|
||||
/* .origin_protocol */ LWSMPRO_CALLBACK, /* dynamic */
|
||||
/* .mountpoint_len */ 4, /* char count */
|
||||
/* .basic_auth_login_file */ NULL,
|
||||
};
|
||||
|
||||
/* default mount serves the URL space from ./mount-origin */
|
||||
|
||||
static const struct lws_http_mount mount = {
|
||||
/* .mount_next */ &mount_dyn, /* linked-list "next" */
|
||||
/* .mountpoint */ "/", /* mountpoint URL */
|
||||
/* .origin */ "./mount-origin", /* serve from dir */
|
||||
/* .def */ "index.html", /* default filename */
|
||||
/* .protocol */ NULL,
|
||||
/* .cgienv */ NULL,
|
||||
/* .extra_mimetypes */ NULL,
|
||||
/* .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, char **argv)
|
||||
{
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_context *context;
|
||||
int n = 0;
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
|
||||
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
|
||||
info.port = 7681;
|
||||
info.protocols = protocols;
|
||||
info.mounts = &mount;
|
||||
|
||||
lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_USER
|
||||
/* | LLL_INFO */ /* | LLL_DEBUG */, NULL);
|
||||
|
||||
lwsl_user("LWS minimal http server dynamic | visit http://localhost:7681\n");
|
||||
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<meta charset="UTF-8">
|
||||
<html>
|
||||
<body>
|
||||
<img src="libwebsockets.org-logo.png"><br>
|
||||
<h1>404</h1>
|
||||
Sorry, that file doesn't exist.
|
||||
</body>
|
||||
</html>
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,16 @@
|
|||
<meta charset="UTF-8">
|
||||
<html>
|
||||
<body>
|
||||
<img src="libwebsockets.org-logo.png"><br>
|
||||
|
||||
Hello from the <b>minimal http server dynamic content example</b>.
|
||||
<p>
|
||||
This is a static page served from ./mount-origin/index.html.
|
||||
<p>
|
||||
Stuff down /dyn in the URL space is generated dynamically<br>
|
||||
by the callback. For example, click on
|
||||
<a href="/dyn/anything" target=_blank>/dyn/anything</a> to
|
||||
see dynamic content.
|
||||
</body>
|
||||
</html>
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
Loading…
Add table
Reference in a new issue