parent
4d27dc49ac
commit
e4d8ce13ef
|
@ -16,7 +16,7 @@ you can choose CyaSSl(wolfssl).
|
||||||
* use [libev] as its event backend
|
* use [libev] as its event backend
|
||||||
* support HTTPS: alternative OpenSSL and CyaSSl(wolfssl)
|
* support HTTPS: alternative OpenSSL and CyaSSl(wolfssl)
|
||||||
* flexible and you can easily extend your application to have HTTP/HTTPS services
|
* flexible and you can easily extend your application to have HTTP/HTTPS services
|
||||||
* Lua template: embed LUA code into HTML code, like embedding PHP to HTML(soon be supported)
|
* Lua template: embed LUA code into HTML code, like embedding PHP to HTML
|
||||||
* code structure is concise and understandable, also suitable for learning
|
* code structure is concise and understandable, also suitable for learning
|
||||||
|
|
||||||
# Why use [libev] as its backend?
|
# Why use [libev] as its backend?
|
||||||
|
|
|
@ -15,7 +15,7 @@ SSL后端可以选择OpenSSL和CyaSSl(wolfssl),如果你对大小敏感,那
|
||||||
* 使用[libev]作为其事件后端
|
* 使用[libev]作为其事件后端
|
||||||
* 支持HTTPS: 可以选择OpenSSL和CyaSSl(wolfssl)
|
* 支持HTTPS: 可以选择OpenSSL和CyaSSl(wolfssl)
|
||||||
* 可伸缩:你可以非常方便的扩展你的应用程序,使之具备HTTP/HTTPS服务
|
* 可伸缩:你可以非常方便的扩展你的应用程序,使之具备HTTP/HTTPS服务
|
||||||
* Lua模板:嵌入LUA代码到HTML中,就像嵌入PHP到HTML中一样(即将支持)
|
* Lua模板:嵌入LUA代码到HTML中,就像嵌入PHP到HTML中一样
|
||||||
* 代码结构简洁通俗易懂,亦适合学习
|
* 代码结构简洁通俗易懂,亦适合学习
|
||||||
|
|
||||||
# 为什么使用[libev]作为其事件后端?
|
# 为什么使用[libev]作为其事件后端?
|
||||||
|
|
|
@ -1,2 +1,6 @@
|
||||||
add_executable(helloworld helloworld.c)
|
add_executable(helloworld helloworld.c)
|
||||||
target_link_libraries(helloworld uhttp)
|
target_link_libraries(helloworld uhttp)
|
||||||
|
|
||||||
|
add_executable(lua_template lua_template.c)
|
||||||
|
target_link_libraries(lua_template uhttp)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
#include <ev.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <uhttp.h>
|
||||||
|
|
||||||
|
static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
|
||||||
|
{
|
||||||
|
printf("Got signal: %d\n", w->signum);
|
||||||
|
ev_break(loop, EVBREAK_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lua_template(struct uh_connection *con)
|
||||||
|
{
|
||||||
|
uh_template(con);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct ev_loop *loop = EV_DEFAULT;
|
||||||
|
ev_signal *sig_watcher = NULL;
|
||||||
|
struct uh_server *srv = NULL;
|
||||||
|
|
||||||
|
uh_log_info("libuhttp version: %s", uh_version());
|
||||||
|
|
||||||
|
sig_watcher = calloc(1, sizeof(ev_signal));
|
||||||
|
if (!sig_watcher)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ev_signal_init(sig_watcher, signal_cb, SIGINT);
|
||||||
|
ev_signal_start(loop, sig_watcher);
|
||||||
|
|
||||||
|
srv = uh_server_new(loop, "0.0.0.0", 8000);
|
||||||
|
if (!srv) {
|
||||||
|
uh_log_err("uh_server_new failed");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (UHTTP_SSL_ENABLED)
|
||||||
|
if (uh_ssl_init(srv, "server-cert.pem", "server-key.pem") < 0)
|
||||||
|
goto err;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uh_register_default_hook(srv, lua_template);
|
||||||
|
|
||||||
|
uh_log_info("Listen on 8000...");
|
||||||
|
|
||||||
|
ev_run(loop, 0);
|
||||||
|
|
||||||
|
err:
|
||||||
|
free(sig_watcher);
|
||||||
|
uh_server_free(srv);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,11 @@ set(UHTTP_VERSION_PATCH 1)
|
||||||
find_package(Libev REQUIRED)
|
find_package(Libev REQUIRED)
|
||||||
find_package(OpenSSL)
|
find_package(OpenSSL)
|
||||||
find_package(CyaSSL)
|
find_package(CyaSSL)
|
||||||
|
find_package(Lua)
|
||||||
|
|
||||||
include_directories(${LIBEV_INCLUDE_DIRS})
|
include_directories(${LIBEV_INCLUDE_DIR} ${LUA_INCLUDE_DIR})
|
||||||
set(EXTRA_LIBS ${LIBEV_LIBRARIES})
|
set(EXTRA_LIBS ${LIBEV_LIBRARIES} ${LUA_LIBRARIES})
|
||||||
set(SOURCE_FILES uhttp.c log.c buf.c ssl.c parser.c)
|
set(SOURCE_FILES uhttp.c log.c buf.c ssl.c parser.c template.c)
|
||||||
|
|
||||||
set(UHTTP_DEBUG_CONFIG 0)
|
set(UHTTP_DEBUG_CONFIG 0)
|
||||||
option(UHTTP_DEBUG "Turn on debug" OFF)
|
option(UHTTP_DEBUG "Turn on debug" OFF)
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
#ifndef _UHTTP_INTERNAL_H
|
#ifndef _UHTTP_INTERNAL_H
|
||||||
#define _UHTTP_INTERNAL_H
|
#define _UHTTP_INTERNAL_H
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include "uhttp/uhttp.h"
|
#include "uhttp/uhttp.h"
|
||||||
|
|
||||||
|
@ -52,6 +54,8 @@ struct uh_server {
|
||||||
#if (UHTTP_SSL_ENABLED)
|
#if (UHTTP_SSL_ENABLED)
|
||||||
void *ssl_ctx;
|
void *ssl_ctx;
|
||||||
#endif
|
#endif
|
||||||
|
char *docroot;
|
||||||
|
lua_State *L;
|
||||||
ev_io read_watcher;
|
ev_io read_watcher;
|
||||||
struct ev_loop *loop;
|
struct ev_loop *loop;
|
||||||
uh_hookfn_t default_cb;
|
uh_hookfn_t default_cb;
|
||||||
|
@ -81,6 +85,8 @@ struct uh_connection {
|
||||||
unsigned char flags;
|
unsigned char flags;
|
||||||
struct uh_buf read_buf;
|
struct uh_buf read_buf;
|
||||||
struct uh_buf write_buf;
|
struct uh_buf write_buf;
|
||||||
|
ev_child child_watcher;
|
||||||
|
ev_io read_watcher_lua;
|
||||||
ev_io read_watcher;
|
ev_io read_watcher;
|
||||||
ev_io write_watcher;
|
ev_io write_watcher;
|
||||||
ev_timer timer_watcher;
|
ev_timer timer_watcher;
|
||||||
|
|
|
@ -38,6 +38,12 @@ struct uh_server *uh_server_new(struct ev_loop *loop, const char *ipaddr, int po
|
||||||
/* frees a uhttp server instance. */
|
/* frees a uhttp server instance. */
|
||||||
void uh_server_free(struct uh_server *srv);
|
void uh_server_free(struct uh_server *srv);
|
||||||
|
|
||||||
|
/* Sets document root. */
|
||||||
|
void uh_set_docroot(struct uh_server *srv, const char *path);
|
||||||
|
|
||||||
|
/* Processing Lua templates */
|
||||||
|
void uh_template(struct uh_connection *con);
|
||||||
|
|
||||||
/* Sends data to the connection. */
|
/* Sends data to the connection. */
|
||||||
int uh_send(struct uh_connection *con, const void *buf, int len);
|
int uh_send(struct uh_connection *con, const void *buf, int len);
|
||||||
|
|
||||||
|
@ -111,11 +117,11 @@ struct uh_str *uh_get_query(struct uh_connection *con);
|
||||||
struct uh_str uh_get_var(struct uh_connection *con, const char *name);
|
struct uh_str uh_get_var(struct uh_connection *con, const char *name);
|
||||||
struct uh_str *uh_get_header(struct uh_connection *con, const char *name);
|
struct uh_str *uh_get_header(struct uh_connection *con, const char *name);
|
||||||
|
|
||||||
int uh_get_con_sock(struct uh_connection *con);
|
|
||||||
|
|
||||||
/* Unescapes strings like '%7B1,%202,%203%7D' would become '{1, 2, 3}' */
|
/* Unescapes strings like '%7B1,%202,%203%7D' would become '{1, 2, 3}' */
|
||||||
int uh_unescape(const char *str, int len, char *out, int olen);
|
int uh_unescape(const char *str, int len, char *out, int olen);
|
||||||
|
|
||||||
|
int uh_get_con_sock(struct uh_connection *con);
|
||||||
|
|
||||||
#if (UHTTP_SSL_ENABLED)
|
#if (UHTTP_SSL_ENABLED)
|
||||||
/* Init ssl for the server */
|
/* Init ssl for the server */
|
||||||
int uh_ssl_init(struct uh_server *srv, const char *cert, const char *key);
|
int uh_ssl_init(struct uh_server *srv, const char *cert, const char *key);
|
||||||
|
|
|
@ -0,0 +1,677 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
|
||||||
|
*
|
||||||
|
* based on https://git.lede-project.org/project/luci.git modules/luci-base/src/template_parser.c
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lualib.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
#include "uhttp/uhttp.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 */
|
||||||
|
{ "io.write(\"", "\")" }, /* T_TYPE_TEXT */
|
||||||
|
{ NULL, NULL }, /* T_TYPE_COMMENT */
|
||||||
|
{ "io.write(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 */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 void template_read_cb(struct ev_loop *loop, ev_io *w, int revents)
|
||||||
|
{
|
||||||
|
struct uh_connection *con = container_of(w, struct uh_connection, read_watcher_lua);
|
||||||
|
char buf[1024] = "";
|
||||||
|
int len;
|
||||||
|
|
||||||
|
len = read(w->fd, buf, sizeof(buf));
|
||||||
|
if (len > 0)
|
||||||
|
uh_send_chunk(con, buf, len);
|
||||||
|
else if (len == 0) {
|
||||||
|
uh_send_chunk(con, NULL, 0);
|
||||||
|
close(w->fd);
|
||||||
|
ev_io_stop(con->srv->loop, w);
|
||||||
|
|
||||||
|
if (!(con->flags & UH_CON_CLOSE))
|
||||||
|
con->flags |= UH_CON_REUSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void child_cb(struct ev_loop *loop, ev_child *w, int revents)
|
||||||
|
{
|
||||||
|
struct uh_connection *con = container_of(w, struct uh_connection, child_watcher);
|
||||||
|
ev_child_stop(con->srv->loop, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uh_template(struct uh_connection *con)
|
||||||
|
{
|
||||||
|
struct template_parser *parser;
|
||||||
|
lua_State *L = con->srv->L;
|
||||||
|
pid_t pid;
|
||||||
|
int pipefd[2];
|
||||||
|
static char path[PATH_MAX] = "";
|
||||||
|
struct stat st;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
socklen_t addrlen = sizeof(addr);
|
||||||
|
struct uh_str *ustr;
|
||||||
|
|
||||||
|
strcpy(path, con->srv->docroot);
|
||||||
|
strncat(path, con->req.path.at, con->req.path.len);
|
||||||
|
|
||||||
|
if (stat(path, &st) < 0) {
|
||||||
|
uh_send_error(con, HTTP_STATUS_NOT_FOUND, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uh_log_debug("Path:%s", path);
|
||||||
|
|
||||||
|
|
||||||
|
if (!L) {
|
||||||
|
L = luaL_newstate();
|
||||||
|
if (!L) {
|
||||||
|
uh_log_err("cannot create LUA state: not enough memory");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
con->srv->L = L;
|
||||||
|
luaL_openlibs(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add all variables to the global environment of LUA.
|
||||||
|
* eg. <%=_UHTTP["REMOTE_HOST"]%>
|
||||||
|
*/
|
||||||
|
|
||||||
|
lua_newtable(L);
|
||||||
|
|
||||||
|
lua_pushstring(L, uh_get_method_str(con));
|
||||||
|
lua_setfield(L, -2, "HTTP_METHOD");
|
||||||
|
|
||||||
|
getpeername(con->sock, (struct sockaddr *)&addr, &addrlen);
|
||||||
|
lua_pushstring(L, inet_ntoa(addr.sin_addr));
|
||||||
|
lua_setfield(L, -2, "REMOTE_HOST");
|
||||||
|
|
||||||
|
ustr = uh_get_url(con);
|
||||||
|
lua_pushlstring(L, ustr->at, ustr->len);
|
||||||
|
lua_setfield(L, -2, "HTTP_URL");
|
||||||
|
|
||||||
|
ustr = uh_get_path(con);
|
||||||
|
lua_pushlstring(L, ustr->at, ustr->len);
|
||||||
|
lua_setfield(L, -2, "HTTP_PATH");
|
||||||
|
|
||||||
|
lua_setglobal(L, "_UHTTP");
|
||||||
|
|
||||||
|
if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) < 0) {
|
||||||
|
uh_log_err("pipe");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
uh_send_head(con, HTTP_STATUS_OK, -1, "Pragma: no-cache\r\nCache-Control: no-cache\r\n");
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
switch (pid) {
|
||||||
|
case -1:
|
||||||
|
uh_log_err("fork");
|
||||||
|
goto err;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
close(0);
|
||||||
|
close(1);
|
||||||
|
close(pipefd[0]);
|
||||||
|
dup2(pipefd[1], 1);
|
||||||
|
|
||||||
|
parser = template_open(path);
|
||||||
|
if (!parser) {
|
||||||
|
uh_log_err("template_open failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ((template_L_do_parse(L, parser, path) != 1) || lua_pcall(L, 0, 0, 0)) {
|
||||||
|
uh_printf_chunk(con, "<h2><b>Lua Error</b></h2>\n%s\n", lua_tostring(L, -1));
|
||||||
|
uh_printf_chunk(con, "</body></html>\n");
|
||||||
|
uh_send_chunk(con, NULL, 0);
|
||||||
|
lua_pop(L, -1);
|
||||||
|
} else {
|
||||||
|
uh_send_chunk(con, NULL, 0);
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
close(pipefd[1]);
|
||||||
|
ev_io_init(&con->read_watcher_lua, template_read_cb, pipefd[0], EV_READ);
|
||||||
|
ev_io_start(con->srv->loop, &con->read_watcher_lua);
|
||||||
|
|
||||||
|
ev_child_init(&con->child_watcher, child_cb, pid, 0);
|
||||||
|
ev_child_start(con->srv->loop, &con->child_watcher);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
err:
|
||||||
|
uh_send_error(con, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
|
||||||
|
}
|
23
src/uhttp.c
23
src/uhttp.c
|
@ -21,6 +21,7 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "uhttp/uhttp.h"
|
#include "uhttp/uhttp.h"
|
||||||
|
@ -346,9 +347,12 @@ struct uh_server *uh_server_new(struct ev_loop *loop, const char *ipaddr, int po
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
int sock = -1, on = 1;
|
int sock = -1, on = 1;
|
||||||
ev_io *read_watcher;
|
ev_io *read_watcher;
|
||||||
|
char buf[PATH_MAX] = "";
|
||||||
|
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_port = htons(port);
|
addr.sin_port = htons(port);
|
||||||
|
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
if (inet_pton(AF_INET, ipaddr, &addr.sin_addr) <= 0) {
|
if (inet_pton(AF_INET, ipaddr, &addr.sin_addr) <= 0) {
|
||||||
uh_log_err("invalid ipaddr");
|
uh_log_err("invalid ipaddr");
|
||||||
|
@ -360,6 +364,14 @@ struct uh_server *uh_server_new(struct ev_loop *loop, const char *ipaddr, int po
|
||||||
uh_log_err("calloc");
|
uh_log_err("calloc");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!realpath(".", buf)) {
|
||||||
|
uh_log_err("Unable to determine work dir");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srv->docroot = strdup(buf);
|
||||||
|
uh_log_debug("docroot:%s", srv->docroot);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&srv->hooks);
|
INIT_LIST_HEAD(&srv->hooks);
|
||||||
INIT_LIST_HEAD(&srv->connections);
|
INIT_LIST_HEAD(&srv->connections);
|
||||||
|
@ -401,7 +413,10 @@ void uh_server_free(struct uh_server *srv)
|
||||||
if (srv) {
|
if (srv) {
|
||||||
struct uh_connection *con, *tmp_c;
|
struct uh_connection *con, *tmp_c;
|
||||||
struct uh_hook *h, *tmp_h;
|
struct uh_hook *h, *tmp_h;
|
||||||
|
|
||||||
|
if (srv->docroot)
|
||||||
|
free(srv->docroot);
|
||||||
|
|
||||||
if (srv->sock > 0)
|
if (srv->sock > 0)
|
||||||
close(srv->sock);
|
close(srv->sock);
|
||||||
|
|
||||||
|
@ -423,6 +438,12 @@ void uh_server_free(struct uh_server *srv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void uh_set_docroot(struct uh_server *srv, const char *path)
|
||||||
|
{
|
||||||
|
srv->docroot = strdup(path);
|
||||||
|
uh_log_debug("docroot:%s", srv->docroot);
|
||||||
|
}
|
||||||
|
|
||||||
int uh_send(struct uh_connection *con, const void *buf, int len)
|
int uh_send(struct uh_connection *con, const void *buf, int len)
|
||||||
{
|
{
|
||||||
len = uh_buf_append(&con->write_buf, buf, len);
|
len = uh_buf_append(&con->write_buf, buf, len);
|
||||||
|
|
Loading…
Reference in New Issue