From 81de054b07f4dc2d65556800bc928ee732c43fd3 Mon Sep 17 00:00:00 2001 From: Jianhui Zhao Date: Sat, 10 Feb 2018 00:42:38 +0800 Subject: [PATCH] Support Lua template Signed-off-by: Jianhui Zhao --- README.md | 7 +- README_ZH.md | 6 +- example/CMakeLists.txt | 3 + example/template.c | 92 ++++++ example/template.html | 11 + src/CMakeLists.txt | 17 +- src/client.c | 16 +- src/client.h | 2 + src/config.h.in | 1 + src/file.c | 8 +- src/file.h | 1 + src/lua_template.c | 650 +++++++++++++++++++++++++++++++++++++++++ src/uhttpd.h | 9 +- 13 files changed, 805 insertions(+), 18 deletions(-) create mode 100755 example/template.c create mode 100755 example/template.html create mode 100755 src/lua_template.c diff --git a/README.md b/README.md index c3bcbc6..90d87d1 100755 --- a/README.md +++ b/README.md @@ -21,21 +21,22 @@ [mbedtls]: https://github.com/ARMmbed/mbedtls [CyaSSl(wolfssl)]: https://github.com/wolfSSL/wolfssl -A very tiny and fast HTTP server library based on [libubox] and referenced from [uhttpd] for Embedded Linux. +A Lightweight and fully asynchronous HTTP server library based on [libubox] and referenced +from [uhttpd] for Embedded Linux. `Keep Watching for More Actions on This Space` # Features * Action - processes requests by invoking registered C functions which mapped to a specific path. -* Tiny and fast +* Lightweight and fully asynchronous * Use [libubox] as its event backend * Support HTTPS - OpenSSL, mbedtls and CyaSSl(wolfssl) * Flexible - you can easily extend your application to have HTTP/HTTPS services * Code structure is concise and understandable, also suitable for learning +* Lua Template - Embed Lua code into HTML code, like embedding PHP into HTML # TO DO * Lua API - Using Lua programming -* Lua Template - Embed Lua code into HTML code, like embedding PHP into HTML # Dependencies * [libubox] diff --git a/README_ZH.md b/README_ZH.md index dddc41b..595586a 100755 --- a/README_ZH.md +++ b/README_ZH.md @@ -21,21 +21,21 @@ [mbedtls]: https://github.com/ARMmbed/mbedtls [CyaSSl(wolfssl)]: https://github.com/wolfSSL/wolfssl -一个专门针对嵌入式Linux的非常小巧且快速的HTTP服务器C库,基于[libubox],参考了[uhttpd]。 +一个轻量的全异步的HTTP服务器C库,基于[libubox],参考了[uhttpd]。 `请保持关注以获取最新的项目动态` # 特性 * Action - 通过调用映射到特定路径的已注册C函数来处理请求。 -* 小巧且快速 +* 轻量、全异步 * 使用[libubox]作为其事件后端 * 支持HTTPS - OpenSSL, mbedtls 和 CyaSSl(wolfssl) * 可伸缩 - 你可以非常方便的扩展你的应用程序,使之具备HTTP/HTTPS服务 * 代码结构简洁通俗易懂,亦适合学习 +* Lua模板 - 嵌入LUA代码到HTML中,就像嵌入PHP到HTML中一样 # 计划支持 * Lua API - 使用Lua编程 -* Lua模板 - 嵌入LUA代码到HTML中,就像嵌入PHP到HTML中一样 # 依赖 * [libubox] diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 6c573a4..97869d9 100755 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -2,3 +2,6 @@ include_directories(${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src) add_executable(helloworld helloworld.c) target_link_libraries(helloworld uhttpd ${LIBUBOX_LIBRARY}) + +add_executable(template template.c) +target_link_libraries(template uhttpd ${LIBUBOX_LIBRARY}) diff --git a/example/template.c b/example/template.c new file mode 100755 index 0000000..789e418 --- /dev/null +++ b/example/template.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2017 Jianhui Zhao + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +static void hello_action(struct uh_client *cl) +{ + uh_template(cl); +} + +static void usage(const char *prog) +{ + fprintf(stderr, "Usage: %s [option]\n" + " -p port # Default port is 8080\n" + " -s # SSl on\n" + " -v # verbose\n", prog); + exit(1); +} + +int main(int argc, char **argv) +{ + struct uh_server *srv = NULL; + bool verbose = false; + bool ssl = false; + int port = 8080; + int opt; + + while ((opt = getopt(argc, argv, "p:vs")) != -1) { + switch (opt) + { + case 'p': + port = atoi(optarg); + break; + case 's': + ssl = true; + break; + case 'v': + verbose = true; + break; + default: /* '?' */ + usage(argv[0]); + } + } + + if (!verbose) + ulog_threshold(LOG_ERR); + + ULOG_INFO("libuhttpd version: %s\n", UHTTPD_VERSION_STRING); + + uloop_init(); + + srv = uh_server_new("0.0.0.0", port); + if (!srv) + goto done; + +#if (!UHTTPD_SSL_SUPPORT) + if (ssl) + ULOG_ERR("SSl is not compiled in\n"); +#else + if (ssl && srv->ssl_init(srv, "server-key.pem", "server-cert.pem") < 0) + goto done; +#endif + + ULOG_INFO("Listen on: %s *:%d\n", srv->ssl ? "https" : "http", port); + + srv->add_action(srv, "/template.html", hello_action); + + uloop_run(); +done: + uloop_done(); + srv->free(srv); + + return 0; +} diff --git a/example/template.html b/example/template.html new file mode 100755 index 0000000..68b8858 --- /dev/null +++ b/example/template.html @@ -0,0 +1,11 @@ +

HTTP_VERSION: <%=_uhttpd.HTTP_VERSION%>

+

HTTP_METHOD: <%=_uhttpd.HTTP_METHOD%>

+

REMOTE_HOST: <%=_uhttpd.REMOTE_HOST%>

+

HTTP_URL: <%=_uhttpd.HTTP_URL%>

+

HTTP_PATH: <%=_uhttpd.HTTP_PATH%>

+<%for k, v in pairs(_uhttpd.headers) do%> +

<%=k%>:<%=v%>

+<%end%> +<%for k, v in pairs(_uhttpd.variables) do%> +

<%=k%>:<%=v%>

+<%end%> diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 653e80a..c13be57 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,5 @@ -add_definitions(-O -Wall -Werror --std=gnu99 -D_GNU_SOURCE -Wno-misleading-indentation) +add_definitions(-O -Wall -Werror --std=gnu99 -D_GNU_SOURCE -Wno-misleading-indentation -Wno-pointer-to-int-cast) # The version number. set(UHTTPD_VERSION_MAJOR 1) @@ -8,21 +8,31 @@ set(UHTTPD_VERSION_PATCH 5) # Check the third party Libraries find_package(Libubox REQUIRED) +find_package(Lua) -include_directories(${CMAKE_CURRENT_BINARY_DIR} ${LIBUBOX_INCLUDE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${LIBUBOX_INCLUDE_DIR} ${LUA_INCLUDE_DIR}) -set(EXTRA_LIBS ${LIBUBOX_LIBRARY} dl) +set(EXTRA_LIBS ${LIBUBOX_LIBRARY} dl ${LUA_LIBRARY}) set(SOURCE_FILES uhttpd.c client.c log.c utils.c file.c action.c) set(UHTTPD_SSL_SUPPORT_CONFIG 1) option(UHTTPD_SSL_SUPPORT "SSL support" ON) +set(UHTTPD_LUA_SUPPORT_CONFIG 1) +option(UHTTPD_LUA_SUPPORT "LUA support" ON) + if(UHTTPD_SSL_SUPPORT) list(APPEND SOURCE_FILES uh_ssl.c) else() set(UHTTPD_SSL_SUPPORT_CONFIG 0) endif() +if(UHTTPD_LUA_SUPPORT) + list(APPEND SOURCE_FILES lua_template.c) +else() + set(UHTTPD_LUA_SUPPORT_CONFIG 0) +endif() + add_library(uhttpd SHARED ${SOURCE_FILES}) set_target_properties(uhttpd PROPERTIES VERSION ${UHTTPD_VERSION_MAJOR}.${UHTTPD_VERSION_MINOR}.${UHTTPD_VERSION_PATCH}) target_link_libraries(uhttpd ${EXTRA_LIBS}) @@ -48,4 +58,5 @@ install( message("") message(STATUS "UHTTPD_VERSION: ${UHTTPD_VERSION_MAJOR}.${UHTTPD_VERSION_MINOR}.${UHTTPD_VERSION_PATCH}") message(STATUS "UHTTPD_SSL_SUPPORT: ${UHTTPD_SSL_SUPPORT}") +message(STATUS "UHTTPD_LUA_SUPPORT: ${UHTTPD_LUA_SUPPORT}") message("") diff --git a/src/client.c b/src/client.c index 0190e11..4f80348 100755 --- a/src/client.c +++ b/src/client.c @@ -28,13 +28,13 @@ #include "uh_ssl.h" #include "log.h" -const char *const http_versions[] = { +static const char *const http_versions[] = { [UH_HTTP_VER_0_9] = "HTTP/0.9", [UH_HTTP_VER_1_0] = "HTTP/1.0", [UH_HTTP_VER_1_1] = "HTTP/1.1" }; -const char *const http_methods[] = { +static const char *const http_methods[] = { [UH_HTTP_MSG_GET] = "GET", [UH_HTTP_MSG_POST] = "POST", [UH_HTTP_MSG_HEAD] = "HEAD" @@ -104,6 +104,16 @@ static void client_send_error(struct uh_client *cl, int code, const char *summar cl->request_done(cl); } +static inline const char *client_get_version(struct uh_client *cl) +{ + return http_versions[cl->request.version]; +} + +static inline const char *client_get_method(struct uh_client *cl) +{ + return http_methods[cl->request.method]; +} + static inline const char *client_get_peer_addr(struct uh_client *cl) { return inet_ntoa(cl->peer_addr.sin_addr); @@ -542,6 +552,8 @@ void uh_accept_client(struct uh_server *srv, bool ssl) cl->chunk_printf = uh_chunk_printf; cl->chunk_vprintf = uh_chunk_vprintf; + cl->get_version = client_get_version; + cl->get_method = client_get_method; cl->get_peer_addr = client_get_peer_addr; cl->get_url = client_get_url; cl->get_path = client_get_path; diff --git a/src/client.h b/src/client.h index b46a184..a30056c 100755 --- a/src/client.h +++ b/src/client.h @@ -111,6 +111,8 @@ struct uh_client { void (*chunk_printf)(struct uh_client *cl, const char *format, ...); void (*chunk_vprintf)(struct uh_client *cl, const char *format, va_list arg); + const char *(*get_method)(struct uh_client *cl); + const char *(*get_version)(struct uh_client *cl); const char *(*get_peer_addr)(struct uh_client *cl); const char *(*get_url)(struct uh_client *cl); const char *(*get_path)(struct uh_client *cl); diff --git a/src/config.h.in b/src/config.h.in index b0d6c03..af78a52 100755 --- a/src/config.h.in +++ b/src/config.h.in @@ -24,5 +24,6 @@ #define UHTTPD_VERSION_STRING "@UHTTPD_VERSION_MAJOR@.@UHTTPD_VERSION_MINOR@.@UHTTPD_VERSION_PATCH@" #define UHTTPD_SSL_SUPPORT @UHTTPD_SSL_SUPPORT_CONFIG@ +#define UHTTPD_LUA_SUPPORT @UHTTPD_LUA_SUPPORT_CONFIG@ #endif diff --git a/src/file.c b/src/file.c index f66eecb..eb55e86 100755 --- a/src/file.c +++ b/src/file.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "file.h" #include "utils.h" @@ -144,13 +145,12 @@ next: /* Returns NULL on error. ** NB: improperly encoded URL should give client 400 [Bad Syntax]; returning ** NULL here causes 404 [Not Found], but that's not too unreasonable. */ -struct path_info *uh_path_lookup(struct uh_client *cl, const char *url) +struct path_info *uh_path_lookup(struct uh_client *cl, const char *path) { static char buf[PATH_MAX]; static char path_phys[PATH_MAX]; static char path_info[PATH_MAX]; static struct path_info p; - const char *path = cl->get_path(cl); const char *query = cl->get_query(cl); const char *docroot = cl->srv->docroot; @@ -161,10 +161,6 @@ struct path_info *uh_path_lookup(struct uh_client *cl, const char *url) int i = 0; int len; - /* back out early if url is undefined */ - if (url == NULL) - return NULL; - memset(&p, 0, sizeof(p)); path_phys[0] = 0; path_info[0] = 0; diff --git a/src/file.h b/src/file.h index 146e914..14a4f51 100755 --- a/src/file.h +++ b/src/file.h @@ -36,6 +36,7 @@ struct mimetype { const char *mime; }; +struct path_info *uh_path_lookup(struct uh_client *cl, const char *path); bool handle_file_request(struct uh_client *cl, const char *path); #endif \ No newline at end of file diff --git a/src/lua_template.c b/src/lua_template.c new file mode 100755 index 0000000..c7dfbcc --- /dev/null +++ b/src/lua_template.c @@ -0,0 +1,650 @@ +/* + * Copyright (C) 2017 Jianhui Zhao + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "file.h" +#include "uhttpd.h" + +/* code types */ +#define T_TYPE_INIT 0 +#define T_TYPE_TEXT 1 +#define T_TYPE_COMMENT 2 +#define T_TYPE_EXPR 3 +#define T_TYPE_INCLUDE 4 +#define T_TYPE_CODE 5 +#define T_TYPE_EOF 6 + +/* leading and trailing code for different types */ +static const char *gen_code[9][2] = { + { NULL, NULL }, /* T_TYPE_INIT */ + { "uh_send(\"", "\")" }, /* T_TYPE_TEXT */ + { NULL, NULL }, /* T_TYPE_COMMENT */ + { "uh_send(tostring(", " or \"\"))" }, /* T_TYPE_EXPR */ + { "include(\"", "\")" }, /* T_TYPE_INCLUDE */ + { NULL, " " }, /* T_TYPE_CODE */ + { NULL, NULL }, /* T_TYPE_EOF */ +}; + +/* buffer object */ +struct template_buffer { + char *data; + char *dptr; + unsigned int size; + unsigned int fill; +}; + +struct template_chunk { + const char *s; + const char *e; + int type; + int line; +}; + +/* parser state */ +struct template_parser { + int fd; + uint32_t size; + char *data; + char *off; + char *gc; + int line; + int in_expr; + int strip_before; + int strip_after; + struct template_chunk prv_chunk; + struct template_chunk cur_chunk; + const char *file; +}; + +/* initialize a buffer object */ +static struct template_buffer *buf_init(int size) +{ + struct template_buffer *buf; + + if (size <= 0) + size = 1024; + + buf = (struct template_buffer *)malloc(sizeof(struct template_buffer)); + + if (buf != NULL) + { + buf->fill = 0; + buf->size = size; + buf->data = malloc(buf->size); + + if (buf->data != NULL) { + buf->dptr = buf->data; + buf->data[0] = 0; + return buf; + } + free(buf); + } + + return NULL; +} + +/* grow buffer */ +static int buf_grow(struct template_buffer *buf, int size) +{ + unsigned int off = (buf->dptr - buf->data); + char *data; + + if (size <= 0) + size = 1024; + + data = realloc(buf->data, buf->size + size); + + if (data != NULL) { + buf->data = data; + buf->dptr = data + off; + buf->size += size; + return buf->size; + } + + return 0; +} + +/* put one char into buffer object */ +static int buf_putchar(struct template_buffer *buf, char c) +{ + if( ((buf->fill + 1) >= buf->size) && !buf_grow(buf, 0) ) + return 0; + + *(buf->dptr++) = c; + *(buf->dptr) = 0; + + buf->fill++; + return 1; +} + +/* append data to buffer */ +static int buf_append(struct template_buffer *buf, const char *s, int len) +{ + if ((buf->fill + len + 1) >= buf->size) { + if (!buf_grow(buf, len + 1)) + return 0; + } + + memcpy(buf->dptr, s, len); + buf->fill += len; + buf->dptr += len; + + *(buf->dptr) = 0; + + return len; +} + +/* read buffer length */ +static int buf_length(struct template_buffer *buf) +{ + return buf->fill; +} + +/* destroy buffer object and return pointer to data */ +static char *buf_destroy(struct template_buffer *buf) +{ + char *data = buf->data; + + free(buf); + return data; +} + +static void luastr_escape(struct template_buffer *out, const char *s, unsigned int l, + int escape_xml) +{ + int esl; + char esq[8]; + char *ptr; + + for (ptr = (char *)s; ptr < (s + l); ptr++) { + switch (*ptr) { + case '\\': + buf_append(out, "\\\\", 2); + break; + + case '"': + if (escape_xml) + buf_append(out, """, 5); + else + buf_append(out, "\\\"", 2); + break; + + case '\n': + buf_append(out, "\\n", 2); + break; + + case '\'': + case '&': + case '<': + case '>': + if (escape_xml) { + esl = snprintf(esq, sizeof(esq), "&#%i;", *ptr); + buf_append(out, esq, esl); + break; + } + + default: + buf_putchar(out, *ptr); + } + } +} + +/* Simple strstr() like function that takes len arguments for both haystack and needle. */ +static char *strfind(char *haystack, int hslen, const char *needle, int ndlen) +{ + int match = 0; + int i, j; + + for( i = 0; i < hslen; i++ ) { + if( haystack[i] == needle[0] ) { + match = ((ndlen == 1) || ((i + ndlen) <= hslen)); + + for( j = 1; (j < ndlen) && ((i + j) < hslen); j++ ) { + if( haystack[i+j] != needle[j] ) { + match = 0; + break; + } + } + + if( match ) + return &haystack[i]; + } + } + + return NULL; +} + + +static int template_error(lua_State *L, struct template_parser *parser) +{ + const char *err = luaL_checkstring(L, -1); + const char *off = parser->prv_chunk.s; + const char *ptr; + char msg[1024]; + int line = 0; + int chunkline = 0; + + if ((ptr = strfind((char *)err, strlen(err), "]:", 2)) != NULL) { + chunkline = atoi(ptr + 2) - parser->prv_chunk.line; + + while (*ptr) { + if (*ptr++ == ' ') { + err = ptr; + break; + } + } + } + + if (strfind((char *)err, strlen(err), "'char(27)'", 10) != NULL) { + off = parser->data + parser->size; + err = "'%>' expected before end of file"; + chunkline = 0; + } + + for (ptr = parser->data; ptr < off; ptr++) + if (*ptr == '\n') + line++; + + snprintf(msg, sizeof(msg), "Syntax error in %s:%d: %s", + parser->file ? parser->file : "[string]", line + chunkline, err ? err : "(unknown error)"); + + lua_pushnil(L); + lua_pushinteger(L, line + chunkline); + lua_pushstring(L, msg); + + return 3; +} + + +static void template_close(struct template_parser *parser) +{ + if (!parser) + return; + + if (parser->gc != NULL) + free(parser->gc); + + /* if file is not set, we were parsing a string */ + if (parser->file) { + if ((parser->data != NULL) && (parser->data != MAP_FAILED)) + munmap(parser->data, parser->size); + + if (parser->fd >= 0) + close(parser->fd); + } + + free(parser); +} + +static void template_text(struct template_parser *parser, const char *e) +{ + const char *s = parser->off; + + if (s < (parser->data + parser->size)) { + if (parser->strip_after) { + while ((s <= e) && isspace(*s)) + s++; + } + + parser->cur_chunk.type = T_TYPE_TEXT; + } else { + parser->cur_chunk.type = T_TYPE_EOF; + } + + parser->cur_chunk.line = parser->line; + parser->cur_chunk.s = s; + parser->cur_chunk.e = e; +} + +static void template_code(struct template_parser *parser, const char *e) +{ + const char *s = parser->off; + + parser->strip_before = 0; + parser->strip_after = 0; + + if (*s == '-') { + parser->strip_before = 1; + for (s++; (s <= e) && (*s == ' ' || *s == '\t'); s++); + } + + if (*(e-1) == '-') { + parser->strip_after = 1; + for (e--; (e >= s) && (*e == ' ' || *e == '\t'); e--); + } + + switch (*s) { + /* comment */ + case '#': + s++; + parser->cur_chunk.type = T_TYPE_COMMENT; + break; + + /* include */ + case '+': + s++; + parser->cur_chunk.type = T_TYPE_INCLUDE; + break; + + /* expr */ + case '=': + s++; + parser->cur_chunk.type = T_TYPE_EXPR; + break; + + /* code */ + default: + parser->cur_chunk.type = T_TYPE_CODE; + break; + } + + parser->cur_chunk.line = parser->line; + parser->cur_chunk.s = s; + parser->cur_chunk.e = e; +} + +static const char *template_format_chunk(struct template_parser *parser, size_t *sz) +{ + const char *s, *p; + const char *head, *tail; + struct template_chunk *c = &parser->prv_chunk; + struct template_buffer *buf; + + *sz = 0; + s = parser->gc = NULL; + + if (parser->strip_before && c->type == T_TYPE_TEXT) { + while ((c->e > c->s) && isspace(*(c->e - 1))) + c->e--; + } + + /* empty chunk */ + if (c->s == c->e) { + if (c->type == T_TYPE_EOF) { + *sz = 0; + s = NULL; + } else { + *sz = 1; + s = " "; + } + } else if ((buf = buf_init(c->e - c->s)) != NULL) { /* format chunk */ + if ((head = gen_code[c->type][0]) != NULL) + buf_append(buf, head, strlen(head)); + + switch (c->type) { + case T_TYPE_TEXT: + luastr_escape(buf, c->s, c->e - c->s, 0); + break; + + case T_TYPE_EXPR: + buf_append(buf, c->s, c->e - c->s); + for (p = c->s; p < c->e; p++) + parser->line += (*p == '\n'); + break; + + case T_TYPE_INCLUDE: + luastr_escape(buf, c->s, c->e - c->s, 0); + break; + + case T_TYPE_CODE: + buf_append(buf, c->s, c->e - c->s); + for (p = c->s; p < c->e; p++) + parser->line += (*p == '\n'); + break; + } + + if ((tail = gen_code[c->type][1]) != NULL) + buf_append(buf, tail, strlen(tail)); + + *sz = buf_length(buf); + s = parser->gc = buf_destroy(buf); + + if (!*sz) { + *sz = 1; + s = " "; + } + } + + return s; +} + +static const char *template_reader(lua_State *L, void *ud, size_t *sz) +{ + struct template_parser *parser = ud; + int rem = parser->size - (parser->off - parser->data); + char *tag; + + parser->prv_chunk = parser->cur_chunk; + + /* free previous string */ + if (parser->gc) { + free(parser->gc); + parser->gc = NULL; + } + + /* before tag */ + if (!parser->in_expr) { + if ((tag = strfind(parser->off, rem, "<%", 2)) != NULL) { + template_text(parser, tag); + parser->off = tag + 2; + parser->in_expr = 1; + } else { + template_text(parser, parser->data + parser->size); + parser->off = parser->data + parser->size; + } + } else { /* inside tag */ + if ((tag = strfind(parser->off, rem, "%>", 2)) != NULL) { + template_code(parser, tag); + parser->off = tag + 2; + parser->in_expr = 0; + } else { + /* unexpected EOF */ + template_code(parser, parser->data + parser->size); + + *sz = 1; + return "\033"; + } + } + + return template_format_chunk(parser, sz); +} + +static int template_L_do_parse(lua_State *L, struct template_parser *parser, const char *chunkname) +{ + int lua_status, rv; + + if (!parser) { + lua_pushnil(L); + lua_pushinteger(L, errno); + lua_pushstring(L, strerror(errno)); + return 3; + } + +#if LUA_VERSION_NUM > 501 + lua_status = lua_load(L, template_reader, parser, chunkname, NULL); +#else + lua_status = lua_load(L, template_reader, parser, chunkname); +#endif + if (lua_status == 0) + rv = 1; + else + rv = template_error(L, parser); + + template_close(parser); + + return rv; +} + +static struct template_parser *template_open(const char *file) +{ + struct stat s; + struct template_parser *parser; + + if (!(parser = malloc(sizeof(*parser)))) + goto err; + + memset(parser, 0, sizeof(*parser)); + parser->fd = -1; + parser->file = file; + + if (stat(file, &s)) + goto err; + + if ((parser->fd = open(file, O_RDONLY)) < 0) + goto err; + + parser->size = s.st_size; + parser->data = mmap(NULL, parser->size, PROT_READ, MAP_PRIVATE, + parser->fd, 0); + + if (parser->data != MAP_FAILED) { + parser->off = parser->data; + parser->cur_chunk.type = T_TYPE_INIT; + parser->cur_chunk.s = parser->data; + parser->cur_chunk.e = parser->data; + + return parser; + } + +err: + template_close(parser); + return NULL; +} + + +static int uh_lua_send(lua_State *L) +{ + const char *buf; + size_t len; + + buf = luaL_checklstring(L, 1, &len); + if (len > 0) { + struct uh_client *cl; + + lua_getglobal(L, "__cl"); + cl = (struct uh_client *)lua_tointeger(L, -1); + cl->chunk_send(cl, buf, len); + } + + lua_pushnumber(L, len); + + return 1; +} + +void uh_template(struct uh_client *cl) +{ + struct template_parser *parser; + lua_State *L = cl->srv->L; + const char *path = cl->get_path(cl); + struct path_info *pi; + const char *name, *value; + + pi = uh_path_lookup(cl, path); + if (!pi) { + if (cl->srv->error404_cb) { + cl->srv->error404_cb(cl); + return; + } + + cl->send_error(cl, 404, "Not Found", "The requested PATH %s was not found on this server.", path); + return; + } + + if (!L) { + L = luaL_newstate(); + if (!L) { + uh_log_err("cannot create LUA state: not enough memory\n"); + goto err; + } + + cl->srv->L = L; + luaL_openlibs(L); + + lua_pushcfunction(L, uh_lua_send); + lua_setglobal(L, "uh_send"); + } + + lua_pushinteger(L, (uint32_t)cl); + lua_setglobal(L, "__cl"); + + lua_newtable(L); + + lua_pushstring(L, cl->get_version(cl)); + lua_setfield(L, -2, "HTTP_VERSION"); + + lua_pushstring(L, cl->get_method(cl)); + lua_setfield(L, -2, "HTTP_METHOD"); + + lua_pushstring(L, cl->get_peer_addr(cl)); + lua_setfield(L, -2, "REMOTE_HOST"); + + lua_pushstring(L, cl->get_url(cl)); + lua_setfield(L, -2, "HTTP_URL"); + + lua_pushstring(L, cl->get_path(cl)); + lua_setfield(L, -2, "HTTP_PATH"); + + lua_newtable(L); + kvlist_for_each(&cl->request.header, name, value) { + lua_pushstring(L, name); + lua_pushstring(L, value); + lua_settable(L, -3); + } + lua_setfield(L, -2, "headers"); + + lua_newtable(L); + kvlist_for_each(&cl->request.var, name, value) { + lua_pushstring(L, name); + lua_pushstring(L, value); + lua_settable(L, -3); + } + lua_setfield(L, -2, "variables"); + + lua_setglobal(L, "_uhttpd"); + + + cl->send_header(cl, 200, "OK", -1); + cl->append_header(cl, "Pragma", "no-cache"); + cl->append_header(cl, "Cache-Control", "no-cache"); + cl->header_end(cl); + + parser = template_open(pi->phys); + if ((template_L_do_parse(L, parser, pi->phys) != 1) || lua_pcall(L, 0, 0, 0)) { + cl->chunk_printf(cl, "

Lua Error

\n%s\n", lua_tostring(L, -1)); + cl->chunk_printf(cl, "\n"); + lua_pop(L, -1); + } + + cl->request_done(cl); + return; +err: + cl->send_error(cl, 500, "Internal Server Error", NULL); +} diff --git a/src/uhttpd.h b/src/uhttpd.h index cc94a97..7dcff6c 100755 --- a/src/uhttpd.h +++ b/src/uhttpd.h @@ -39,9 +39,16 @@ struct uh_server { #if (UHTTPD_SSL_SUPPORT) int (*ssl_init)(struct uh_server *srv, const char *key, const char *crt); -#endif +#endif +#if (UHTTPD_LUA_SUPPORT) + void *L; +#endif }; struct uh_server *uh_server_new(const char *host, int port); +#if (UHTTPD_LUA_SUPPORT) + void uh_template(struct uh_client *cl); +#endif + #endif