1
0
Fork 0
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:
Andy Green 2018-03-20 10:31:53 +08:00
parent 2519ac9ced
commit 5ecec970b2
11 changed files with 389 additions and 2 deletions

View file

@ -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",

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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()

View file

@ -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.

View file

@ -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;
}

View file

@ -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

View file

@ -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