Use libev instead of libubox

Signed-off-by: Jianhui Zhao <jianhuizhao329@gmail.com>
main
Jianhui Zhao 2019-08-27 10:21:37 +08:00
parent b8248347fa
commit 471e4fe436
38 changed files with 4602 additions and 3520 deletions

3
.gitmodules vendored 100644
View File

@ -0,0 +1,3 @@
[submodule "src/buffer"]
path = src/buffer
url = https://github.com/zhaojh329/buffer

View File

@ -4,7 +4,6 @@ project(libuhttpd C)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules/")
#set(CMAKE_VERBOSE_MAKEFILE ON)
add_subdirectory(src)
add_subdirectory(example)

View File

@ -0,0 +1,29 @@
# - Try to find libev
# Once done this will define
# LIBEV_FOUND - System has libev
# LIBEV_INCLUDE_DIR - The libev include directories
# LIBEV_LIBRARY - The libraries needed to use libev
find_path(LIBEV_INCLUDE_DIR ev.h PATH_SUFFIXES libev)
find_library(LIBEV_LIBRARY ev PATH_SUFFIXES lib64)
if(LIBEV_INCLUDE_DIR)
file(STRINGS "${LIBEV_INCLUDE_DIR}/ev.h"
LIBEV_VERSION_MAJOR REGEX "^#define[ \t]+EV_VERSION_MAJOR[ \t]+[0-9]+")
file(STRINGS "${LIBEV_INCLUDE_DIR}/ev.h"
LIBEV_VERSION_MINOR REGEX "^#define[ \t]+EV_VERSION_MINOR[ \t]+[0-9]+")
string(REGEX REPLACE "[^0-9]+" "" LIBEV_VERSION_MAJOR "${LIBEV_VERSION_MAJOR}")
string(REGEX REPLACE "[^0-9]+" "" LIBEV_VERSION_MINOR "${LIBEV_VERSION_MINOR}")
set(LIBEV_VERSION "${LIBEV_VERSION_MAJOR}.${LIBEV_VERSION_MINOR}")
unset(LIBEV_VERSION_MINOR)
unset(LIBEV_VERSION_MAJOR)
endif()
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set LIBEV_FOUND to TRUE
# if all listed variables are TRUE and the requested version matches.
find_package_handle_standard_args(Libev REQUIRED_VARS
LIBEV_LIBRARY LIBEV_INCLUDE_DIR
VERSION_VAR LIBEV_VERSION)
mark_as_advanced(LIBEV_INCLUDE_DIR LIBEV_LIBRARY)

View File

@ -1,17 +0,0 @@
# - Try to find libubox
# Once done this will define
# LIBUBOX_FOUND - System has libubox
# LIBUBOX_INCLUDE_DIR - The libubox include directories
# LIBUBOX_LIBRARY - The libraries needed to use libubox
find_path(LIBUBOX_INCLUDE_DIR libubox)
find_library(LIBUBOX_LIBRARY ubox PATH_SUFFIXES lib64)
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set LIBUBOX_FOUND to TRUE
# if all listed variables are TRUE and the requested version matches.
find_package_handle_standard_args(Libubox REQUIRED_VARS
LIBUBOX_LIBRARY LIBUBOX_INCLUDE_DIR
VERSION_VAR LIBUBOX_VERSION)
mark_as_advanced(LIBUBOX_INCLUDE_DIR LIBUBOX_LIBRARY)

0
cmake/Modules/FindLibuhttpd.cmake 100755 → 100644
View File

View File

@ -0,0 +1,37 @@
find_path(MBEDTLS_INCLUDE_DIR mbedtls/ssl.h)
find_library(MBEDTLS_LIBRARY mbedtls)
find_library(MBEDX509_LIBRARY mbedx509)
find_library(MBEDCRYPTO_LIBRARY mbedcrypto)
if(MBEDTLS_INCLUDE_DIR)
file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h"
MBEDTLS_VERSION_MAJOR REGEX "^#define[ \t]+MBEDTLS_VERSION_MAJOR[ \t]+[0-9]+")
file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h"
MBEDTLS_VERSION_MINOR REGEX "^#define[ \t]+MBEDTLS_VERSION_MINOR[ \t]+[0-9]+")
file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h"
MBEDTLS_VERSION_PATCH REGEX "^#define[ \t]+MBEDTLS_VERSION_PATCH[ \t]+[0-9]+")
string(REGEX REPLACE "[^0-9]+" "" MBEDTLS_VERSION_MAJOR "${MBEDTLS_VERSION_MAJOR}")
string(REGEX REPLACE "[^0-9]+" "" MBEDTLS_VERSION_MINOR "${MBEDTLS_VERSION_MINOR}")
string(REGEX REPLACE "[^0-9]+" "" MBEDTLS_VERSION_PATCH "${MBEDTLS_VERSION_PATCH}")
set(MBEDTLS_VERSION "${MBEDTLS_VERSION_MAJOR}.${MBEDTLS_VERSION_MINOR}.${MBEDTLS_VERSION_PATCH}")
unset(MBEDTLS_VERSION_MINOR)
unset(MBEDTLS_VERSION_MAJOR)
unset(MBEDTLS_VERSION_PATCH)
endif()
set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}" "${MBEDCRYPTO_LIBRARY}")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MbedTLS
REQUIRED_VARS
MBEDTLS_LIBRARY
MBEDX509_LIBRARY
MBEDCRYPTO_LIBRARY
MBEDTLS_INCLUDE_DIR
VERSION_VAR
MBEDTLS_VERSION
)
mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)

View File

@ -0,0 +1,62 @@
if(WOLFSSL_PREFER_STATIC_LIB)
set(WOLFSSL_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
if(WIN32)
set(CMAKE_FIND_LIBRARY_SUFFIXES .a .lib ${CMAKE_FIND_LIBRARY_SUFFIXES})
else()
set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
endif()
endif()
if(UNIX)
find_package(PkgConfig QUIET)
pkg_check_modules(_WOLFSSL QUIET wolfssl)
endif()
find_path(WOLFSSL_INCLUDE_DIR NAMES wolfssl/version.h HINTS ${_WOLFSSL_INCLUDEDIR})
find_library(WOLFSSL_LIBRARY NAMES wolfssl HINTS ${_WOLFSSL_LIBDIR})
if(WOLFSSL_INCLUDE_DIR AND WOLFSSL_LIBRARY)
set(WOLFSSL_INCLUDE_DIR ${WOLFSSL_INCLUDE_DIR})
set(WOLFSSL_LIBRARY ${WOLFSSL_LIBRARY})
set(WOLFSSL_VERSION ${_WOLFSSL_VERSION})
set(WOLFSSL_IS_WOLFSSL ON)
else()
if(UNIX)
pkg_check_modules(_WOLFSSL QUIET WOLFSSL)
endif()
find_path(WOLFSSL_INCLUDE_DIR NAMES WOLFSSL/version.h HINTS ${_WOLFSSL_INCLUDEDIR})
find_library(WOLFSSL_LIBRARY NAMES WOLFSSL HINTS ${_WOLFSSL_LIBDIR})
set(WOLFSSL_VERSION ${_WOLFSSL_VERSION})
set(WOLFSSL_IS_WOLFSSL OFF)
endif()
if(NOT WOLFSSL_VERSION AND WOLFSSL_INCLUDE_DIR)
if(WOLFSSL_IS_WOLFSSL)
file(STRINGS "${WOLFSSL_INCLUDE_DIR}/wolfssl/version.h" WOLFSSL_VERSION_STR REGEX "^#define[\t ]+LIBWOLFSSL_VERSION_STRING[\t ]+\"[^\"]+\"")
else()
file(STRINGS "${WOLFSSL_INCLUDE_DIR}/WOLFSSL/version.h" WOLFSSL_VERSION_STR REGEX "^#define[\t ]+LIBWOLFSSL_VERSION_STRING[\t ]+\"[^\"]+\"")
endif()
if(WOLFSSL_VERSION_STR MATCHES "\"([^\"]+)\"")
set(WOLFSSL_VERSION "${CMAKE_MATCH_1}")
endif()
endif()
set(WOLFSSL_INCLUDE_DIRS ${WOLFSSL_INCLUDE_DIR})
set(WOLFSSL_LIBRARIES ${WOLFSSL_LIBRARY})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(WOLFSSL
REQUIRED_VARS
WOLFSSL_LIBRARY
WOLFSSL_INCLUDE_DIR
VERSION_VAR
WOLFSSL_VERSION
)
mark_as_advanced(WOLFSSL_INCLUDE_DIR WOLFSSL_LIBRARY WOLFSSL_INCLUDE_DIR WOLFSSL_LIBRARY)
if(WOLFSSL_PREFER_STATIC_LIB)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${WOLFSSL_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
unset(WOLFSSL_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES)
endif()

View File

@ -1,7 +1,4 @@
include_directories(${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src)
include_directories(${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/buffer ${CMAKE_BINARY_DIR}/src ${LIBEV_INCLUDE_DIR})
add_executable(helloworld helloworld.c)
target_link_libraries(helloworld uhttpd ${LIBUBOX_LIBRARY})
add_executable(template template.c)
target_link_libraries(template uhttpd ${LIBUBOX_LIBRARY})
add_executable(example example.c)
target_link_libraries(example uhttpd ${LIBEV_LIBRARY})

118
example/example.c 100644
View File

@ -0,0 +1,118 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "uhttpd.h"
static void on_request(struct uh_connection *conn)
{
int body_len;
const char *body = conn->get_body(conn, &body_len);
conn->send_head(conn, 200, -1, NULL);
conn->chunk_printf(conn, "I'm Libuhttpd: %s\n", UHTTPD_VERSION_STRING);
conn->chunk_printf(conn, "Url: %s\n", conn->get_url(conn));
conn->chunk_printf(conn, "User-Agent: %s\n", conn->get_header(conn, "User-Agent"));
conn->chunk_printf(conn, "Body: %.*s\n", body_len, body);
conn->chunk_end(conn);
}
static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
{
if (w->signum == SIGINT) {
ev_break(loop, EVBREAK_ALL);
uh_log_info("Normal quit\n");
}
}
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 ev_loop *loop = EV_DEFAULT;
struct ev_signal signal_watcher;
struct uh_server *srv = NULL;
bool verbose = false;
bool ssl = false;
int port = 8080;
int opt;
while ((opt = getopt(argc, argv, "p:sv")) != -1) {
switch (opt)
{
case 'p':
port = atoi(optarg);
break;\
case 's':
ssl = true;
case 'v':
verbose = true;
break;
default: /* '?' */
usage(argv[0]);
}
}
if (!verbose)
uh_log_threshold(LOG_ERR);
uh_log_info("libuhttpd version: %s\n", UHTTPD_VERSION_STRING);
srv = uh_server_new(loop, "0.0.0.0", port);
if (!srv)
return -1;
#if UHTTPD_SSL_SUPPORT
if (ssl && srv->ssl_init(srv, "server-cert.pem", "server-key.pem") < 0)
goto err;
#endif
srv->on_request = on_request;
uh_log_info("Listen on: *:%d\n", port);
ev_signal_init(&signal_watcher, signal_cb, SIGINT);
ev_signal_start(loop, &signal_watcher);
ev_run(loop, 0);
err:
srv->free(srv);
free(srv);
return 0;
}

View File

@ -1,124 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <uhttpd.h>
#include <string.h>
#include <libubox/ulog.h>
static void on_accept(struct uh_client *cl)
{
ULOG_INFO("New connection from: %s:%d\n", cl->get_peer_addr(cl), cl->get_peer_port(cl));
}
static int on_request(struct uh_client *cl)
{
const char *path = cl->get_path(cl);
int body_len = 0;
if (strcmp(path, "/hello"))
return UH_REQUEST_CONTINUE;
cl->send_header(cl, 200, "OK", -1);
cl->append_header(cl, "Myheader", "Hello");
cl->header_end(cl);
cl->chunk_printf(cl, "<h1>Hello Libuhttpd %s</h1>", UHTTPD_VERSION_STRING);
cl->chunk_printf(cl, "<h1>REMOTE_ADDR: %s</h1>", cl->get_peer_addr(cl));
cl->chunk_printf(cl, "<h1>URL: %s</h1>", cl->get_url(cl));
cl->chunk_printf(cl, "<h1>PATH: %s</h1>", cl->get_path(cl));
cl->chunk_printf(cl, "<h1>QUERY: %s</h1>", cl->get_query(cl));
cl->chunk_printf(cl, "<h1>VAR name: %s</h1>", cl->get_var(cl, "name"));
cl->chunk_printf(cl, "<h1>BODY:%s</h1>", cl->get_body(cl, &body_len));
cl->request_done(cl);
return UH_REQUEST_DONE;
}
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->on_accept = on_accept;
srv->on_request = on_request;
uloop_run();
done:
uloop_done();
if (srv) {
srv->free(srv);
free(srv);
}
return 0;
}

View File

@ -1,110 +0,0 @@
#!/usr/bin/env lua
--[[
Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
--]]
local uloop = require "uloop"
local uh = require "uhttpd"
local verbose = true
local port = 8914
uloop.init()
-- LOG_DEBUG LOG_INFO LOG_ERR
if not verbose then
uh.set_log_threshold(uh.LOG_ERR)
end
uh.log(uh.LOG_INFO, "uhttpd version:" .. uh.VERSION)
local srv = uh.new(port)
-- srv:ssl_init("uhttpd.crt", "uhttpd.key")
-- srv:set_options({docroot = "/home/zjh/www", index = "lua.html"})
uh.log(uh.LOG_INFO, "Listen on:" .. port)
local http_methods = {
[uh.HTTP_METHOD_GET] = "GET",
[uh.HTTP_METHOD_POST] = "POST",
[uh.HTTP_METHOD_HEAD] = "HEAD"
}
local http_version = {
[uh.HTTP_VER_09] = "HTTP/0.9",
[uh.HTTP_VER_10] = "HTTP/1.0",
[uh.HTTP_VER_11] = "HTTP/1.1"
}
srv:on_error404(function(cl, path)
cl:send_header(200, "OK", -1)
cl:header_end()
cl:chunk_send(string.format("<h1>Libuhttpd-Lua: '%s' Not found</h1>", path))
cl:request_done()
end)
srv:on_request(function(cl, path)
if path ~= "/hello" then
return uh.REQUEST_CONTINUE
end
cl:send_header(200, "OK", -1)
cl:append_header("Myheader", "Hello")
cl:header_end()
cl:chunk_send(string.format("<h1>Hello Libuhttpd %s</h1>", uh.VERSION))
cl:chunk_send(string.format("<h1>REMOTE_ADDR: %s</h1>", cl:get_remote_addr()))
cl:chunk_send(string.format("<h1>METHOD: %s</h1>", http_methods[cl:get_http_method()]))
cl:chunk_send(string.format("<h1>HTTP Version: %s</h1>", http_version[cl:get_http_version()]))
cl:chunk_send(string.format("<h1>URL: %s</h1>", cl:get_url()))
cl:chunk_send(string.format("<h1>QUERY: %s</h1>", cl:get_query() or ""))
cl:chunk_send(string.format("<h1>Body: %s</h1>", cl:get_body() or ""))
-- Get a http var
local var_x = cl:get_var("x")
cl:chunk_send(string.format("<h1>Var x: %s</h1>", var_x or ""))
-- Get a http header
local user_agent = cl:get_header("user-agent")
cl:chunk_send(string.format("<h1>User-Agent: %s</h1>", user_agent))
cl:chunk_send("<hr />")
-- Get all http vars
local vars = cl:get_var()
for k, v in pairs(vars) do
cl:chunk_send(string.format("<h1>%s: %s</h1>", k, v))
end
cl:chunk_send("<hr />")
-- Get all http headers
local headers = cl:get_header()
for k, v in pairs(headers) do
cl:chunk_send(string.format("<h1>%s: %s</h1>", k, v))
end
cl:request_done()
return uh.REQUEST_DONE
end)
uloop.run()

View File

@ -1,114 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <uhttpd.h>
#include <string.h>
#include <libubox/ulog.h>
static int on_request(struct uh_client *cl)
{
const char *path = cl->get_path(cl);
if (strcmp(path, "/template.html"))
return UH_REQUEST_CONTINUE;
#if (UHTTPD_LUA_SUPPORT)
uh_template(cl);
#else
cl->send_header(cl, 200, "OK", -1);
cl->header_end(cl);
cl->chunk_printf(cl, "<h1>Lua not enabled when compile</h1>");
cl->request_done(cl);
#endif
return UH_REQUEST_DONE;
}
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->on_request = on_request;
uloop_run();
done:
uloop_done();
if (srv) {
srv->free(srv);
free(srv);
}
return 0;
}

View File

@ -1,11 +0,0 @@
<h1>HTTP_VERSION: <%=_uhttpd.HTTP_VERSION%></h1>
<h1>HTTP_METHOD: <%=_uhttpd.HTTP_METHOD%></h1>
<h1>REMOTE_HOST: <%=_uhttpd.REMOTE_HOST%></h1>
<h1>HTTP_URL: <%=_uhttpd.HTTP_URL%></h1>
<h1>HTTP_PATH: <%=_uhttpd.HTTP_PATH%></h1>
<%for k, v in pairs(_uhttpd.headers) do%>
<h1><%=k%>:<%=v%></h1>
<%end%>
<%for k, v in pairs(_uhttpd.variables) do%>
<h1><%=k%>:<%=v%></h1>
<%end%>

View File

@ -1,76 +1,120 @@
add_definitions(-O -Wall -Werror --std=gnu99 -D_GNU_SOURCE -Wno-misleading-indentation)
add_definitions(-O -Wall -Werror --std=gnu99 -D_GNU_SOURCE)
# The version number.
set(UHTTPD_VERSION_MAJOR 2)
set(UHTTPD_VERSION_MINOR 1)
set(UHTTPD_VERSION_MAJOR 3)
set(UHTTPD_VERSION_MINOR 3)
set(UHTTPD_VERSION_PATCH 2)
# Check the third party Libraries
find_package(Libubox REQUIRED)
find_package(Lua)
find_package(Libev REQUIRED)
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${LIBUBOX_INCLUDE_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/buffer ${CMAKE_CURRENT_BINARY_DIR} ${LIBEV_INCLUDE_DIR})
set(EXTRA_LIBS ${LIBUBOX_LIBRARY} dl)
set(SOURCE_FILES uhttpd.c client.c log.c utils.c file.c)
set(EXTRA_LIBS ${LIBEV_LIBRARY} dl)
set(SOURCE_FILES uhttpd.c log.c connection.c buffer/buffer.c http_parser.c ssl.c)
set(UHTTPD_SSL_SUPPORT_CONFIG 1)
option(UHTTPD_SSL_SUPPORT "SSL support" ON)
option(UHTTPD_USE_OPENSSL "Force select OpenSSL" OFF)
option(UHTTPD_USE_WOLFSSL "Force select WolfSSL(CyaSSL)" OFF)
option(UHTTPD_USE_MBEDTLS "Force select MbedTLS(PolarSSL)" OFF)
set(LUA_SUPPORT_DEFAULT "ON")
if (NOT LUA_FOUND)
set(LUA_SUPPORT_DEFAULT "OFF")
endif (NOT LUA_FOUND)
set(SSL_NAME OFF)
set(UHTTPD_HAVE_OPENSSL_CONFIG 0)
set(UHTTPD_HAVE_WOLFSSL_CONFIG 0)
set(UHTTPD_HAVE_MBEDTLS_CONFIG 0)
set(UHTTPD_LUA_SUPPORT_CONFIG 1)
option(UHTTPD_LUA_SUPPORT "LUA support" ${LUA_SUPPORT_DEFAULT})
if (UHTTPD_SSL_SUPPORT)
list(APPEND SOURCE_FILES uh_ssl.c)
else ()
if(NOT UHTTPD_SSL_SUPPORT)
set(UHTTPD_SSL_SUPPORT_CONFIG 0)
endif ()
else()
find_package(OpenSSL)
find_package(WolfSSL)
find_package(MbedTLS)
if (UHTTPD_LUA_SUPPORT)
if (NOT LUA_FOUND)
message(FATAL_ERROR "Lua was not found on your system")
endif (NOT LUA_FOUND)
if(UHTTPD_USE_OPENSSL)
if (NOT OPENSSL_FOUND)
set(UHTTPD_SSL_SUPPORT OFF)
message(WARNING "Force select OpenSSL, but not found it")
endif()
elseif(UHTTPD_USE_WOLFSSL)
if (NOT WOLFSSL_FOUND)
set(UHTTPD_SSL_SUPPORT OFF)
message(WARNING "Force select WolfSSL(CyaSSL), but not found it")
endif()
elseif(UHTTPD_USE_MBEDTLS)
if (NOT MBEDTLS_FOUND)
set(UHTTPD_SSL_SUPPORT OFF)
message(WARNING "Force select MbedTLS(PolarSSL), but not found it")
endif()
elseif(OPENSSL_FOUND)
set(UHTTPD_USE_OPENSSL ON)
elseif(WOLFSSL_FOUND)
set(UHTTPD_USE_WOLFSSL ON)
elseif(MBEDTLS_FOUND)
set(UHTTPD_USE_MBEDTLS ON)
else()
set(UHTTPD_SSL_SUPPORT OFF)
message(WARNING "No available SSL libraries found")
endif()
include_directories(${LUA_INCLUDE_DIR})
list(APPEND EXTRA_LIBS ${LUA_LIBRARY})
if(UHTTPD_USE_OPENSSL)
set(SSL_NAME "OpenSSL")
set(SSL_INC ${OPENSSL_INCLUDE_DIR})
set(SSL_LIB ${OPENSSL_LIBRARIES})
set(UHTTPD_HAVE_OPENSSL_CONFIG 1)
elseif(UHTTPD_USE_WOLFSSL)
set(SSL_NAME "WolfSSL(CyaSSL)")
set(SSL_INC ${WOLFSSL_INCLUDE_DIR})
set(SSL_LIB ${WOLFSSL_LIBRARIES})
set(UHTTPD_HAVE_WOLFSSL_CONFIG 1)
elseif(UHTTPD_USE_MBEDTLS)
set(SSL_NAME "MbedTLS(PolarSSL)")
set(SSL_INC ${MBEDTLS_INCLUDE_DIR})
set(SSL_LIB ${MBEDTLS_LIBRARIES})
set(UHTTPD_HAVE_MBEDTLS_CONFIG 1)
endif()
list(APPEND SOURCE_FILES lua_template.c)
add_subdirectory(lua)
else ()
set(UHTTPD_LUA_SUPPORT_CONFIG 0)
endif ()
if(UHTTPD_SSL_SUPPORT)
include_directories(${SSL_INC})
list(APPEND EXTRA_LIBS ${SSL_LIB})
message(STATUS "Select ${SSL_NAME} as the SSL backend")
else()
set(SSL_NAME OFF)
set(UHTTPD_SSL_SUPPORT_CONFIG 0)
endif()
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})
add_library(uhttpd_s STATIC ${SOURCE_FILES})
set_target_properties(uhttpd_s PROPERTIES OUTPUT_NAME uhttpd)
target_link_libraries(uhttpd_s ${EXTRA_LIBS})
# configure a header file to pass some of the CMake settings to the source code
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
install(
FILES
uhttpd.h
connection.h
log.h
buffer/buffer.h
http_parser.h
${CMAKE_CURRENT_BINARY_DIR}/config.h
${CMAKE_CURRENT_SOURCE_DIR}/uhttpd.h
${CMAKE_CURRENT_SOURCE_DIR}/client.h
DESTINATION
include/uhttpd
)
install(
TARGETS uhttpd LIBRARY
DESTINATION lib
TARGETS uhttpd uhttpd_s
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
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("")
message(STATUS "UHTTPD_SSL_SUPPORT: ${SSL_NAME}")

1
src/buffer 160000

@ -0,0 +1 @@
Subproject commit 7b1ba65e2c79be169ef49f1d43524151ab293302

View File

@ -1,652 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include <ctype.h>
#include <assert.h>
#include <arpa/inet.h>
#include <linux/limits.h>
#include <libubox/blobmsg.h>
#include "uhttpd.h"
#include "client.h"
#include "file.h"
#include "utils.h"
#include "uh_ssl.h"
#include "log.h"
static const char *const http_versions[] = {
[UH_HTTP_VER_09] = "HTTP/0.9",
[UH_HTTP_VER_10] = "HTTP/1.0",
[UH_HTTP_VER_11] = "HTTP/1.1"
};
static const char *const http_methods[] = {
[UH_HTTP_METHOD_GET] = "GET",
[UH_HTTP_METHOD_POST] = "POST",
[UH_HTTP_METHOD_HEAD] = "HEAD"
};
static inline void client_send(struct uh_client *cl, const void *data, int len)
{
ustream_write(cl->us, data, len, true);
}
static void client_send_header(struct uh_client *cl, int code, const char *summary, int length)
{
cl->printf(cl, "%s %03i %s\r\n", http_versions[cl->request.version], code, summary);
cl->printf(cl, "Server: Libuhttpd %s\r\n", UHTTPD_VERSION_STRING);
if (length < 0) {
cl->printf(cl, "Transfer-Encoding: chunked\r\n");
} else {
cl->printf(cl, "Content-Length: %d\r\n", length);
}
cl->response_length = length;
}
static inline void client_append_header(struct uh_client *cl, const char *name, const char *value)
{
cl->printf(cl, "%s: %s\r\n", name, value);
}
static inline void client_header_end(struct uh_client *cl)
{
cl->printf(cl, "\r\n");
}
static inline void client_redirect(struct uh_client *cl, int code, const char *fmt, ...)
{
va_list arg;
const char *summary = ((code == 301) ? "Moved Permanently" : "Found");
assert((code == 301 || code == 302) && fmt);
cl->send_header(cl, code, summary, 0);
cl->printf(cl, "Location: ");
va_start(arg, fmt);
cl->vprintf(cl, fmt, arg);
va_end(arg);
cl->printf(cl, "\r\n\r\n");
cl->request_done(cl);
}
static void client_send_error(struct uh_client *cl, int code, const char *summary, const char *fmt, ...)
{
va_list arg;
cl->send_header(cl, code, summary, -1);
cl->printf(cl, "Content-Type: text/html\r\n\r\n");
cl->chunk_printf(cl, "<h1>%s</h1>", summary);
if (fmt) {
va_start(arg, fmt);
cl->chunk_vprintf(cl, fmt, arg);
va_end(arg);
}
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);
}
static inline int client_get_peer_port(struct uh_client *cl)
{
return ntohs(cl->peer_addr.sin_port);
}
static inline const char *client_get_url(struct uh_client *cl)
{
return kvlist_get(&cl->request.url, "url");
}
static inline const char *client_get_path(struct uh_client *cl)
{
return kvlist_get(&cl->request.url, "path");
}
static inline const char *client_get_query(struct uh_client *cl)
{
return kvlist_get(&cl->request.url, "query");
}
const char *client_get_var(struct uh_client *cl, const char *name)
{
return kvlist_get(&cl->request.var, name);
}
static inline const char *client_get_header(struct uh_client *cl, const char *name)
{
return kvlist_get(&cl->request.header, name);
}
static inline const char *client_get_body(struct uh_client *cl, int *len)
{
*len = cl->dispatch.post_len;
return cl->dispatch.body;
}
static int post_post_data(struct uh_client *cl, const char *data, int len)
{
struct dispatch *d = &cl->dispatch;
d->post_len += len;
if (d->post_len > UH_POST_MAX_POST_SIZE)
goto err;
if (d->post_len > UH_POST_DATA_BUF_SIZE) {
d->body = realloc(d->body, UH_POST_MAX_POST_SIZE);
if (!d->body) {
cl->send_error(cl, 500, "Internal Server Error", "No memory");
return 0;
}
}
memcpy(d->body, data, len);
return len;
err:
cl->send_error(cl, 413, "Request Entity Too Large", NULL);
return 0;
}
static void post_post_done(struct uh_client *cl)
{
char *path = kvlist_get(&cl->request.url, "path");
if (cl->srv->on_request(cl) == UH_REQUEST_DONE)
return;
if (handle_file_request(cl, path))
return;
if (cl->srv->on_error404) {
cl->srv->on_error404(cl);
return;
}
cl->send_error(cl, 404, "Not Found", "The requested PATH %s was not found on this server.", path);
}
static void post_data_free(struct uh_client *cl)
{
struct dispatch *d = &cl->dispatch;
free(d->body);
}
static void uh_handle_request(struct uh_client *cl)
{
char *path = kvlist_get(&cl->request.url, "path");
if (cl->srv->on_request) {
struct dispatch *d = &cl->dispatch;
switch (cl->request.method) {
case UH_HTTP_METHOD_GET:
if (cl->srv->on_request(cl) == UH_REQUEST_DONE)
return;
break;
case UH_HTTP_METHOD_POST:
d->post_data = post_post_data;
d->post_done = post_post_done;
d->free = post_data_free;
d->body = calloc(1, UH_POST_DATA_BUF_SIZE);
if (!d->body)
cl->send_error(cl, 500, "Internal Server Error", "No memory");
return;
default:
cl->send_error(cl, 400, "Bad Request", "Invalid Request");
return;
}
}
if (handle_file_request(cl, path))
return;
if (cl->srv->on_error404) {
cl->srv->on_error404(cl);
return;
}
cl->send_error(cl, 404, "Not Found", "The requested PATH %s was not found on this server.", path);
}
static inline void connection_close(struct uh_client *cl)
{
cl->us->eof = true;
cl->state = CLIENT_STATE_CLOSE;
ustream_state_change(cl->us);
}
static inline void keepalive_cb(struct uloop_timeout *timeout)
{
struct uh_client *cl = container_of(timeout, struct uh_client, timeout);
connection_close(cl);
}
static void dispatch_done(struct uh_client *cl)
{
if (cl->dispatch.free)
cl->dispatch.free(cl);
memset(&cl->dispatch, 0, sizeof(struct dispatch));
}
static inline int hdr_get_len(struct kvlist *kv, const void *data)
{
return strlen(data) + 1;
}
static void client_request_done(struct uh_client *cl)
{
if (cl->response_length < 0)
cl->printf(cl, "0\r\n\r\n");
dispatch_done(cl);
if (cl->connection_close) {
connection_close(cl);
return;
}
cl->state = CLIENT_STATE_INIT;
memset(&cl->request, 0, sizeof(cl->request));
memset(&cl->dispatch, 0, sizeof(cl->dispatch));
kvlist_init(&cl->request.url, hdr_get_len);
kvlist_init(&cl->request.var, hdr_get_len);
kvlist_init(&cl->request.header, hdr_get_len);
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
}
static void client_free(struct uh_client *cl)
{
if (cl) {
dispatch_done(cl);
uloop_timeout_cancel(&cl->timeout);
if (cl->srv->ssl)
uh_ssl_client_detach(cl);
ustream_free(&cl->sfd.stream);
shutdown(cl->sfd.fd.fd, SHUT_RDWR);
close(cl->sfd.fd.fd);
list_del(&cl->list);
kvlist_free(&cl->request.url);
kvlist_free(&cl->request.var);
kvlist_free(&cl->request.header);
cl->srv->nclients--;
if (cl->srv->on_client_free)
cl->srv->on_client_free(cl);
free(cl);
}
}
static void parse_var(struct uh_client *cl, char *query)
{
struct kvlist *kv = &cl->request.var;
char *k, *v;
while (query && *query) {
k = query;
query = strchr(query, '&');
if (query)
*query++ = 0;
v = strchr(k, '=');
if (v)
*v++ = 0;
if (*k && v)
kvlist_set(kv, k, v);
}
}
static int client_parse_request(struct uh_client *cl, char *data)
{
struct http_request *req = &cl->request;
char *type, *url, *version, *p;
int h_method, h_version;
static char path[PATH_MAX];
type = strtok(data, " ");
url = strtok(NULL, " ");
version = strtok(NULL, " ");
if (!type || !url || !version)
return CLIENT_STATE_DONE;
h_method = find_idx(http_methods, ARRAY_SIZE(http_methods), type);
h_version = find_idx(http_versions, ARRAY_SIZE(http_versions), version);
if (h_method < 0 || h_version < 0) {
req->version = UH_HTTP_VER_10;
return CLIENT_STATE_DONE;
}
kvlist_set(&req->url, "url", url);
p = strchr(url, '?');
if (p) {
*p = 0;
if (p[1]) {
kvlist_set(&req->url, "query", p + 1);
parse_var(cl, p + 1);
}
}
if (uh_urldecode(path, sizeof(path) - 1, url, strlen(url)) < 0)
return CLIENT_STATE_DONE;
kvlist_set(&req->url, "path", path);
req->method = h_method;
req->version = h_version;
if (req->version < UH_HTTP_VER_11)
cl->connection_close = true;
return CLIENT_STATE_HEADER;
}
static bool client_init_cb(struct uh_client *cl, char *buf, int len)
{
char *newline;
newline = strstr(buf, "\r\n");
if (!newline)
return false;
if (newline == buf) {
ustream_consume(cl->us, 2);
return true;
}
*newline = 0;
cl->state = client_parse_request(cl, buf);
ustream_consume(cl->us, newline + 2 - buf);
if (cl->state == CLIENT_STATE_DONE)
cl->send_error(cl, 400, "Bad Request", NULL);
return true;
}
static void client_poll_post_data(struct uh_client *cl)
{
struct dispatch *d = &cl->dispatch;
struct http_request *r = &cl->request;
char *buf;
int len;
if (cl->state == CLIENT_STATE_DONE)
return;
while (1) {
int cur_len;
buf = ustream_get_read_buf(cl->us, &len);
if (!buf || !len)
break;
if (!d->post_data)
return;
cur_len = min(r->content_length, len);
if (cur_len) {
if (d->post_data)
cur_len = d->post_data(cl, buf, cur_len);
r->content_length -= cur_len;
ustream_consume(cl->us, cur_len);
continue;
}
}
if (!r->content_length && cl->state != CLIENT_STATE_DONE) {
if (cl->dispatch.post_done)
cl->dispatch.post_done(cl);
cl->state = CLIENT_STATE_DONE;
}
}
static inline bool client_data_cb(struct uh_client *cl, char *buf, int len)
{
client_poll_post_data(cl);
return false;
}
static void client_parse_header(struct uh_client *cl, char *data)
{
struct http_request *req = &cl->request;
char *err;
char *name;
char *val;
if (!*data) {
uloop_timeout_cancel(&cl->timeout);
cl->state = CLIENT_STATE_DATA;
uh_handle_request(cl);
return;
}
val = uh_split_header(data);
if (!val) {
cl->state = CLIENT_STATE_DONE;
return;
}
for (name = data; *name; name++)
if (isupper(*name))
*name = tolower(*name);
if (!strcmp(data, "content-length")) {
req->content_length = strtoul(val, &err, 0);
if (err && *err) {
cl->send_error(cl, 400, "Bad Request", "Invalid Content-Length");
return;
}
} else if (!strcmp(data, "transfer-encoding") && !strcmp(val, "chunked")) {
cl->send_error(cl, 501, "Not Implemented", "Chunked body is not implemented");
return;
} else if (!strcmp(data, "connection") && !strcasecmp(val, "close")) {
cl->connection_close = true;
}
kvlist_set(&req->header, data, val);
cl->state = CLIENT_STATE_HEADER;
}
static bool client_header_cb(struct uh_client *cl, char *buf, int len)
{
char *newline;
int line_len;
newline = strstr(buf, "\r\n");
if (!newline)
return false;
*newline = 0;
client_parse_header(cl, buf);
line_len = newline + 2 - buf;
ustream_consume(cl->us, line_len);
if (cl->state == CLIENT_STATE_DATA)
return client_data_cb(cl, newline + 2, len - line_len);
return true;
}
typedef bool (*read_cb_t)(struct uh_client *cl, char *buf, int len);
static read_cb_t read_cbs[] = {
[CLIENT_STATE_INIT] = client_init_cb,
[CLIENT_STATE_HEADER] = client_header_cb,
[CLIENT_STATE_DATA] = client_data_cb,
};
void uh_client_read_cb(struct uh_client *cl)
{
struct ustream *us = cl->us;
char *str;
int len;
do {
str = ustream_get_read_buf(us, &len);
if (!str || !len)
return;
if (cl->state >= ARRAY_SIZE(read_cbs) || !read_cbs[cl->state])
return;
if (!read_cbs[cl->state](cl, str, len)) {
if (len == us->r.buffer_len && cl->state != CLIENT_STATE_DATA)
cl->send_error(cl, 413, "Request Entity Too Large", NULL);
break;
}
} while(1);
}
static inline void client_ustream_read_cb(struct ustream *s, int bytes)
{
struct uh_client *cl = container_of(s, struct uh_client, sfd.stream);
uh_client_read_cb(cl);
}
static void client_ustream_write_cb(struct ustream *s, int bytes)
{
struct uh_client *cl = container_of(s, struct uh_client, sfd.stream);
if (cl->dispatch.write_cb)
cl->dispatch.write_cb(cl);
}
void uh_client_notify_state(struct uh_client *cl)
{
struct ustream *us = cl->us;
if (!us->write_error) {
if (cl->state == CLIENT_STATE_DATA)
return;
if (!us->eof || us->w.data_bytes)
return;
}
client_free(cl);
}
static inline void client_notify_state(struct ustream *s)
{
struct uh_client *cl = container_of(s, struct uh_client, sfd.stream);
uh_client_notify_state(cl);
}
void uh_accept_client(struct uh_server *srv, bool ssl)
{
struct uh_client *cl;
unsigned int sl;
int sfd;
struct sockaddr_in addr;
sl = sizeof(addr);
sfd = accept(srv->fd.fd, (struct sockaddr *)&addr, &sl);
if (sfd < 0) {
uh_log_err("accept");
return;
}
cl = calloc(1, sizeof(struct uh_client));
if (!cl) {
uh_log_err("calloc");
goto err;
}
memcpy(&cl->peer_addr, &addr, sizeof(addr));
cl->us = &cl->sfd.stream;
if (ssl) {
uh_ssl_client_attach(cl);
} else {
cl->us->notify_read = client_ustream_read_cb;
cl->us->notify_write = client_ustream_write_cb;
cl->us->notify_state = client_notify_state;
}
cl->us->string_data = true;
ustream_fd_init(&cl->sfd, sfd);
cl->timeout.cb = keepalive_cb;
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
list_add(&cl->list, &srv->clients);
kvlist_init(&cl->request.url, hdr_get_len);
kvlist_init(&cl->request.var, hdr_get_len);
kvlist_init(&cl->request.header, hdr_get_len);
cl->srv = srv;
cl->srv->nclients++;
cl->free = client_free;
cl->send_error = client_send_error;
cl->send_header = client_send_header;
cl->append_header = client_append_header;
cl->header_end = client_header_end;
cl->redirect = client_redirect;
cl->request_done = client_request_done;
cl->send = client_send;
cl->printf = uh_printf;
cl->vprintf = uh_vprintf;
cl->chunk_send = uh_chunk_send;
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_peer_port = client_get_peer_port;
cl->get_url = client_get_url;
cl->get_path = client_get_path;
cl->get_query = client_get_query;
cl->get_var = client_get_var;
cl->get_header = client_get_header;
cl->get_body = client_get_body;
if (srv->on_accept)
srv->on_accept(cl);
return;
err:
close(sfd);
}

View File

@ -1,136 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#ifndef _CLIENT_H_
#define _CLIENT_H_
#include <netinet/in.h>
#include <libubox/kvlist.h>
#include <libubox/ustream.h>
#include "config.h"
#if (UHTTPD_SSL_SUPPORT)
#include <libubox/ustream-ssl.h>
#endif
#define UHTTPD_CONNECTION_TIMEOUT 30
#define UH_POST_DATA_BUF_SIZE 1024
#define UH_POST_MAX_POST_SIZE 4096
enum request_status {
UH_REQUEST_DONE,
UH_REQUEST_CONTINUE
};
enum http_method {
UH_HTTP_METHOD_GET,
UH_HTTP_METHOD_POST,
UH_HTTP_METHOD_HEAD
};
enum http_version {
UH_HTTP_VER_09,
UH_HTTP_VER_10,
UH_HTTP_VER_11
};
enum client_state {
CLIENT_STATE_INIT,
CLIENT_STATE_HEADER,
CLIENT_STATE_DATA,
CLIENT_STATE_DONE,
CLIENT_STATE_CLOSE
};
struct http_request {
enum http_method method;
enum http_version version;
int content_length;
struct kvlist url;
struct kvlist var;
struct kvlist header;
};
struct uh_client;
struct dispatch {
int (*post_data)(struct uh_client *cl, const char *data, int len);
void (*post_done)(struct uh_client *cl);
void (*write_cb)(struct uh_client *cl);
void (*free)(struct uh_client *cl);
struct {
int fd;
} file;
int post_len;
char *body;
};
struct uh_client {
struct list_head list;
struct uh_server *srv;
struct ustream *us;
struct ustream_fd sfd;
#if (UHTTPD_SSL_SUPPORT)
struct ustream_ssl ssl;
#endif
struct uloop_timeout timeout;
enum client_state state;
struct http_request request;
struct sockaddr_in peer_addr;
struct dispatch dispatch;
bool connection_close;
int response_length;
void (*free)(struct uh_client *cl);
void (*send_error)(struct uh_client *cl, int code, const char *summary, const char *fmt, ...);
void (*send_header)(struct uh_client *cl, int code, const char *summary, int length);
void (*append_header)(struct uh_client *cl, const char *name, const char *value);
void (*header_end)(struct uh_client *cl);
void (*redirect)(struct uh_client *cl, int code, const char *fmt, ...);
void (*request_done)(struct uh_client *cl);
void (*send)(struct uh_client *cl, const void *data, int len);
void (*printf)(struct uh_client *cl, const char *format, ...);
void (*vprintf)(struct uh_client *cl, const char *format, va_list arg);
void (*chunk_send)(struct uh_client *cl, const void *data, int len);
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);
int (*get_peer_port)(struct uh_client *cl);
const char *(*get_url)(struct uh_client *cl);
const char *(*get_path)(struct uh_client *cl);
const char *(*get_query)(struct uh_client *cl);
const char *(*get_var)(struct uh_client *cl, const char *name);
const char *(*get_header)(struct uh_client *cl, const char *name);
const char *(*get_body)(struct uh_client *cl, int *len);
};
void uh_client_read_cb(struct uh_client *cl);
void uh_client_notify_state(struct uh_client *cl);
void uh_accept_client(struct uh_server *srv, bool ssl);
#endif

View File

@ -1,20 +1,25 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
* MIT License
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* Copyright (c) 2019 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* 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
* Lesser General Public License for more details.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _UHTTPD_CONFIG_H
@ -26,6 +31,9 @@
#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@
#define UHTTPD_HAVE_OPENSSL @UHTTPD_HAVE_OPENSSL_CONFIG@
#define UHTTPD_HAVE_WOLFSSL @UHTTPD_HAVE_WOLFSSL_CONFIG@
#define UHTTPD_HAVE_MBEDTLS @UHTTPD_HAVE_MBEDTLS_CONFIG@
#endif

506
src/connection.c 100644
View File

@ -0,0 +1,506 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include "connection.h"
#include "uhttpd.h"
#include "utils.h"
#include "ssl.h"
static void conn_send(struct uh_connection *conn, const void *data, ssize_t len)
{
buffer_put_data(&conn->wb, data, len);
ev_io_start(conn->srv->loop, &conn->iow);
}
static void conn_printf(struct uh_connection *conn, const char *format, ...)
{
struct buffer *wb = &conn->wb;
va_list arg;
va_start(arg, format);
buffer_put_vprintf(wb, format, arg);
va_end(arg);
ev_io_start(conn->srv->loop, &conn->iow);
}
static void conn_vprintf(struct uh_connection *conn, const char *format, va_list arg)
{
buffer_put_vprintf(&conn->wb, format, arg);
ev_io_start(conn->srv->loop, &conn->iow);
}
static void conn_chunk_send(struct uh_connection *conn, const void *data, ssize_t len)
{
conn_printf(conn, "%X\r\n", len);
conn_send(conn, data, len);
conn_printf(conn, "\r\n", len);
}
static void conn_chunk_vprintf(struct uh_connection *conn, const char *format, va_list arg)
{
char buf[256];
va_list arg2;
int len;
va_copy(arg2, arg);
len = vsnprintf(buf, sizeof(buf), format, arg2);
va_end(arg2);
conn_printf(conn, "%X\r\n", len);
if (len < sizeof(buf))
conn_send(conn, buf, len);
else
conn_vprintf(conn, format, arg);
conn_printf(conn, "\r\n", len);
}
static void conn_chunk_printf(struct uh_connection *conn, const char *format, ...)
{
va_list arg;
va_start(arg, format);
conn_chunk_vprintf(conn, format, arg);
va_end(arg);
}
static inline void conn_chunk_end(struct uh_connection *conn)
{
conn_chunk_send(conn, NULL, 0);
}
static void conn_send_status_line(struct uh_connection *conn, int code, const char *extra_headers)
{
conn_printf(conn, "HTTP/1.1 %d %s\r\nServer: Libuhttpd/%s\r\n", code, http_status_str(code), UHTTPD_VERSION_STRING);
if (extra_headers)
conn_send(conn, extra_headers, strlen(extra_headers));
}
static void conn_send_head(struct uh_connection *conn, int code, int content_length, const char *extra_headers)
{
conn_send_status_line(conn, code, extra_headers);
if (content_length < 0)
conn_printf(conn, "%s", "Transfer-Encoding: chunked\r\n");
else
conn_printf(conn, "Content-Length: %d\r\n", content_length);
conn_send(conn, "\r\n", 2);
}
static void conn_error(struct uh_connection *conn, int code, const char *reason)
{
if (!reason)
reason = http_status_str(code);
conn_send_head(conn, code, strlen(reason), "Content-Type: text/plain\r\nConnection: close\r\n");
conn_send(conn, reason, strlen(reason));
conn->flags |= CONN_F_SEND_AND_CLOSE;
}
static void conn_redirect(struct uh_connection *conn, int code, const char *location, ...)
{
struct buffer *wb = &conn->wb;
va_list arg;
assert((code == 301 || code == 302) && location);
conn_send_status_line(conn, code, NULL);
conn_printf(conn, "Location: ");
va_start(arg, location);
buffer_put_vprintf(wb, location, arg);
va_end(arg);
conn_send(conn, "\r\n", 2);
conn_printf(conn, "Content-Length: 0\r\n");
conn_send(conn, "\r\n", 2);
}
/* offset of the request field */
#define ROF(c, a) (a - (const char *)c->rb.data)
/* data of the request field */
#define O2D(c, o) ((const char *)c->rb.data + o)
static const char *conn_get_url(struct uh_connection *conn)
{
struct uh_request *req = &conn->req;
if (!req->url)
req->url = strndup(O2D(conn, req->url_info.offset), req->url_info.len);
return req->url;
}
static const char *conn_get_header(struct uh_connection *conn, const char *name)
{
struct uh_request *req = &conn->req;
int i, j;
for (i = 0; i < UHTTPD_MAX_HEADER_NUM; i++) {
if (!req->headers[i].name)
break;
if (!strcmp(req->headers[i].name, name))
return req->headers[i].value;
}
if (i == UHTTPD_MAX_HEADER_NUM)
return "";
for (j = 0; j < UHTTPD_MAX_HEADER_NUM; j++) {
if (req->headers_info[j].name_offset > 0) {
const char *p = O2D(conn, req->headers_info[j].name_offset);
if (!strncmp(p, name, req->headers_info[j].name_len)) {
req->headers[i].name = strndup(p, req->headers_info[j].name_len);
req->headers[i].value = strndup(O2D(conn, req->headers_info[j].value_offset), req->headers_info[j].value_len);
req->headers_info[j].name_len = 0;
return req->headers[i].value;
}
}
}
return "";
}
static const char *conn_get_body(struct uh_connection *conn, int *len)
{
struct uh_request *req = &conn->req;
const char *at = O2D(conn, req->body.offset);
*len = req->body.len;
return at;
}
static int on_url_cb(struct http_parser *parser, const char *at, size_t length)
{
struct uh_connection *conn = (struct uh_connection *)parser->data;
struct uh_request *req = &conn->req;
req->url_info.offset = ROF(conn, at);
req->url_info.len = length;
return 0;
}
static int on_header_field_cb(struct http_parser *parser, const char *at, size_t length)
{
struct uh_connection *conn = (struct uh_connection *)parser->data;
struct uh_request *req = &conn->req;
int n = req->header_num;
if (n == UHTTPD_MAX_HEADER_NUM) {
uh_log_err("Header too more\n");
return 0;
}
req->headers_info[n].name_offset = ROF(conn, at);
req->headers_info[n].name_len = length;
return 0;
}
static int on_header_value_cb(struct http_parser *parser, const char *at, size_t length)
{
struct uh_connection *conn = (struct uh_connection *)parser->data;
struct uh_request *req = &conn->req;
int n = req->header_num;
req->headers_info[n].value_offset = ROF(conn, at);
req->headers_info[n].value_len = length;
req->header_num++;
return 0;
}
static int on_body_cb(struct http_parser *parser, const char *at, size_t length)
{
struct uh_connection *conn = (struct uh_connection *)parser->data;
struct uh_request *req = &conn->req;
req->body.offset = ROF(conn, at);
req->body.len = length;
return 0;
}
static int on_message_complete_cb(struct http_parser *parser)
{
struct uh_connection *conn = (struct uh_connection *)parser->data;
struct uh_request *req = &conn->req;
int i;
if (conn->srv->on_request)
conn->srv->on_request(conn);
else
conn_error(conn, 404, NULL);
buffer_pull(&conn->rb, NULL, buffer_length(&conn->rb));
if (req->url)
free(req->url);
for (i = 0; i < UHTTPD_MAX_HEADER_NUM; i++) {
if (req->headers[i].name)
free(req->headers[i].name);
if (req->headers[i].value)
free(req->headers[i].value);
}
memset(req, 0, sizeof(struct uh_request));
return 0;
}
static struct http_parser_settings settings = {
.on_url = on_url_cb,
.on_header_field = on_header_field_cb,
.on_header_value = on_header_value_cb,
.on_body = on_body_cb,
.on_message_complete = on_message_complete_cb
};
static void conn_free(struct uh_connection *conn)
{
struct ev_loop *loop = conn->srv->loop;
struct sockaddr_in *addr = &conn->addr;
ev_timer_stop(loop, &conn->timer);
ev_io_stop(loop, &conn->ior);
ev_io_stop(loop, &conn->iow);
buffer_free(&conn->rb);
buffer_free(&conn->wb);
if (conn->prev)
conn->prev->next = conn->next;
else
conn->srv->conns = conn->next;
if (conn->next)
conn->next->prev = conn->prev;
#if UHTTPD_SSL_SUPPORT
uh_ssl_free(conn->ssl);
#endif
if (conn->sock > 0)
close(conn->sock);
uh_log_debug("Connection(%s:%d) closed\n", inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
free(conn);
}
#if UHTTPD_SSL_SUPPORT
static int conn_ssl_write(int fd, void *buf, size_t count, void *ssl)
{
int ret = uh_ssl_write(ssl, buf, count);
if (ret < 0) {
if (ret == UH_SSL_ERROR_AGAIN)
return P_FD_PENDING;
return P_FD_ERR;
}
return ret;
}
#endif
static void conn_write_cb(struct ev_loop *loop, struct ev_io *w, int revents)
{
struct uh_connection *conn = container_of(w, struct uh_connection, iow);
int ret;
#if UHTTPD_SSL_SUPPORT
if (conn->ssl)
ret = buffer_pull_to_fd(&conn->wb, w->fd, buffer_length(&conn->wb), conn_ssl_write, conn->ssl);
else
#endif
ret = buffer_pull_to_fd(&conn->wb, w->fd, buffer_length(&conn->wb), NULL, NULL);
if (ret < 0) {
uh_log_err("write error: %s\n", strerror(errno));
conn_free(conn);
return;
}
if (buffer_length(&conn->wb) == 0) {
if (conn->flags & CONN_F_SEND_AND_CLOSE)
conn_free(conn);
else
ev_io_stop(loop, w);
}
}
#if UHTTPD_SSL_SUPPORT
static int conn_ssl_read(int fd, void *buf, size_t count, void *ssl)
{
int ret = uh_ssl_read(ssl, buf, count);
if (ret < 0) {
if (ret == UH_SSL_ERROR_AGAIN)
return P_FD_PENDING;
return P_FD_ERR;
}
return ret;
}
#endif
static void conn_read_cb(struct ev_loop *loop, struct ev_io *w, int revents)
{
struct uh_connection *conn = container_of(w, struct uh_connection, ior);
struct http_parser *parser = &conn->parser;
static uint8_t sep[] = {'\r', '\n', '\r', '\n'};
struct buffer *rb = &conn->rb;
int ret, length, nparsed;
bool eof;
if (conn->flags & CONN_F_SEND_AND_CLOSE) {
ev_io_stop(loop, w);
return;
}
#if UHTTPD_SSL_SUPPORT
if (conn->ssl && !(conn->flags & CONN_F_SSL_HANDSHAKE_DONE)) {
ret = uh_ssl_handshake(conn->ssl);
if (ret == UH_SSL_ERROR_AGAIN)
return;
if (ret == UH_SSL_ERROR_UNKNOWN) {
conn_free(conn);
return;
}
conn->flags |= CONN_F_SSL_HANDSHAKE_DONE;
}
#endif
conn->activity = ev_now(loop);
#if UHTTPD_SSL_SUPPORT
if (conn->ssl)
ret = buffer_put_fd(rb, w->fd, -1, &eof, conn_ssl_read, conn->ssl);
else
#endif
ret = buffer_put_fd(rb, w->fd, -1, &eof, NULL, NULL);
if (ret < 0) {
conn_error(conn, 500, NULL);
uh_log_err("read error: %s\n", strerror(errno));
return;
}
if (eof) {
conn_free(conn);
return;
}
if (buffer_find(rb, 0, 1024, sep, 4) < 0)
return;
length = buffer_length(rb);
nparsed = http_parser_execute(parser, &settings, (const char *)rb->data, length);
if (parser->upgrade)
conn_error(conn, 501, NULL);
else if (nparsed != length)
conn_error(conn, 400, http_errno_description(parser->http_errno));
}
static void keepalive_cb(struct ev_loop *loop, struct ev_timer *w, int revents)
{
struct uh_connection *conn = container_of(w, struct uh_connection, timer);
ev_tstamp after = conn->activity + UHTTPD_CONNECTION_TIMEOUT - ev_now(loop);
if (conn->flags & CONN_F_SEND_AND_CLOSE) {
ev_timer_stop(loop, w);
return;
}
if (after > 0) {
ev_timer_set (w, after, 0.0);
ev_timer_start (loop, w);
return;
}
conn_error(conn, 408, NULL);
}
struct uh_connection *uh_new_connection(struct uh_server *srv, int sock, struct sockaddr_in *addr)
{
struct uh_connection *conn;
conn = calloc(1, sizeof(struct uh_connection));
if (!conn) {
uh_log_err("malloc: %s\n", strerror(errno));
return NULL;
}
conn->srv = srv;
conn->sock = sock;
conn->activity = ev_now(srv->loop);
memcpy(&conn->addr, addr, sizeof(struct sockaddr_in));
ev_io_init(&conn->iow, conn_write_cb, sock, EV_WRITE);
ev_io_init(&conn->ior, conn_read_cb, sock, EV_READ);
ev_io_start(srv->loop, &conn->ior);
ev_timer_init(&conn->timer, keepalive_cb, UHTTPD_CONNECTION_TIMEOUT, 0.0);
ev_timer_start(srv->loop, &conn->timer);
#if UHTTPD_SSL_SUPPORT
if (srv->ssl_ctx)
conn->ssl = uh_ssl_new(srv->ssl_ctx, sock);
#endif
http_parser_init(&conn->parser, HTTP_REQUEST);
conn->parser.data = conn;
conn->free = conn_free;
conn->send = conn_send;
conn->printf = conn_printf;
conn->vprintf = conn_vprintf;
conn->send_status_line = conn_send_status_line;
conn->send_head = conn_send_head;
conn->error = conn_error;
conn->redirect = conn_redirect;
conn->chunk_send = conn_chunk_send;
conn->chunk_printf = conn_chunk_printf;
conn->chunk_vprintf = conn_chunk_vprintf;
conn->chunk_end = conn_chunk_end;
conn->get_url = conn_get_url;
conn->get_header = conn_get_header;
conn->get_body = conn_get_body;
return conn;
}

104
src/connection.h 100644
View File

@ -0,0 +1,104 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _UH_CONNECTION_H
#define _UH_CONNECTION_H
#include <ev.h>
#include <stdarg.h>
#include <arpa/inet.h>
#include "http_parser.h"
#include "buffer.h"
#include "config.h"
#define UHTTPD_CONNECTION_TIMEOUT 30.0
#define UHTTPD_MAX_HEADER_NUM 20
#define CONN_F_SEND_AND_CLOSE (1 << 0) /* Push remaining data and close */
#define CONN_F_SSL_HANDSHAKE_DONE (1 << 1) /* SSL hanshake has completed */
struct uh_server;
struct uh_request {
struct {
int offset;
int len;
}url_info;
char *url;
struct {
int name_offset;
int name_len;
int value_offset;
int value_len;
}headers_info[UHTTPD_MAX_HEADER_NUM];
int header_num;
struct {
char *name;
char *value;
}headers[UHTTPD_MAX_HEADER_NUM];
struct {
int offset;
int len;
}body;
};
struct uh_connection {
int sock;
#if UHTTPD_SSL_SUPPORT
void *ssl;
#endif
uint8_t flags;
struct ev_io ior;
struct ev_io iow;
struct buffer rb;
struct buffer wb;
ev_tstamp activity;
struct ev_timer timer;
struct uh_request req;
struct uh_server *srv;
struct sockaddr_in addr;
struct http_parser parser;
struct uh_connection *prev;
struct uh_connection *next;
void (*free)(struct uh_connection *conn);
void (*send)(struct uh_connection *conn, const void *data, ssize_t len);
void (*printf)(struct uh_connection *conn, const char *format, ...);
void (*vprintf)(struct uh_connection *conn, const char *format, va_list arg);
void (*send_status_line)(struct uh_connection *conn, int code, const char *extra_headers);
void (*send_head)(struct uh_connection *conn, int code, int content_length, const char *extra_headers);
void (*error)(struct uh_connection *conn, int code, const char *reason);
void (*redirect)(struct uh_connection *conn, int code, const char *location, ...);
void (*chunk_send)(struct uh_connection *conn, const void *data, ssize_t len);
void (*chunk_printf)(struct uh_connection *conn, const char *format, ...);
void (*chunk_vprintf)(struct uh_connection *conn, const char *format, va_list arg);
void (*chunk_end)(struct uh_connection *conn);
const char *(*get_url)(struct uh_connection *conn);
const char *(*get_header)(struct uh_connection *conn, const char *name);
const char *(*get_body)(struct uh_connection *conn, int *len);
};
struct uh_connection *uh_new_connection(struct uh_server *srv, int sock, struct sockaddr_in *addr);
#endif

View File

@ -1,412 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include <stdio.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <limits.h>
#include "file.h"
#include "utils.h"
#include "uhttpd.h"
#include "log.h"
static const struct mimetype uh_mime_types[] = {
{ "txt", "text/plain" },
{ "log", "text/plain" },
{ "js", "text/javascript" },
{ "css", "text/css" },
{ "htm", "text/html" },
{ "html", "text/html" },
{ "diff", "text/x-patch" },
{ "patch", "text/x-patch" },
{ "c", "text/x-csrc" },
{ "h", "text/x-chdr" },
{ "o", "text/x-object" },
{ "ko", "text/x-object" },
{ "bmp", "image/bmp" },
{ "gif", "image/gif" },
{ "png", "image/png" },
{ "jpg", "image/jpeg" },
{ "jpeg", "image/jpeg" },
{ "svg", "image/svg+xml" },
{ "json", "application/json" },
{ "jsonp", "application/javascript" },
{ "zip", "application/zip" },
{ "pdf", "application/pdf" },
{ "xml", "application/xml" },
{ "xsl", "application/xml" },
{ "doc", "application/msword" },
{ "ppt", "application/vnd.ms-powerpoint" },
{ "xls", "application/vnd.ms-excel" },
{ "odt", "application/vnd.oasis.opendocument.text" },
{ "odp", "application/vnd.oasis.opendocument.presentation" },
{ "pl", "application/x-perl" },
{ "sh", "application/x-shellscript" },
{ "php", "application/x-php" },
{ "deb", "application/x-deb" },
{ "iso", "application/x-cd-image" },
{ "tar.gz", "application/x-compressed-tar" },
{ "tgz", "application/x-compressed-tar" },
{ "gz", "application/x-gzip" },
{ "tar.bz2", "application/x-bzip-compressed-tar" },
{ "tbz", "application/x-bzip-compressed-tar" },
{ "bz2", "application/x-bzip" },
{ "tar", "application/x-tar" },
{ "rar", "application/x-rar-compressed" },
{ "mp3", "audio/mpeg" },
{ "ogg", "audio/x-vorbis+ogg" },
{ "wav", "audio/x-wav" },
{ "mpg", "video/mpeg" },
{ "mpeg", "video/mpeg" },
{ "avi", "video/x-msvideo" },
{ "README", "text/plain" },
{ "log", "text/plain" },
{ "cfg", "text/plain" },
{ "conf", "text/plain" },
{ "pac", "application/x-ns-proxy-autoconfig" },
{ "wpad.dat", "application/x-ns-proxy-autoconfig" },
{ NULL, NULL }
};
static char *canonpath(const char *path, char *path_resolved)
{
const char *path_cpy = path;
char *path_res = path_resolved;
/* normalize */
while ((*path_cpy != '\0') && (path_cpy < (path + PATH_MAX - 2))) {
if (*path_cpy != '/')
goto next;
/* skip repeating / */
if (path_cpy[1] == '/') {
path_cpy++;
continue;
}
/* /./ or /../ */
if (path_cpy[1] == '.') {
/* skip /./ */
if ((path_cpy[2] == '/') || (path_cpy[2] == '\0')) {
path_cpy += 2;
continue;
}
/* collapse /x/../ */
if ((path_cpy[2] == '.') &&
((path_cpy[3] == '/') || (path_cpy[3] == '\0'))) {
while ((path_res > path_resolved) && (*--path_res != '/'));
path_cpy += 3;
continue;
}
}
next:
*path_res++ = *path_cpy++;
}
/* remove trailing slash if not root / */
if ((path_res > (path_resolved+1)) && (path_res[-1] == '/'))
path_res--;
else if (path_res == path_resolved)
*path_res++ = '/';
*path_res = '\0';
return path_resolved;
}
/* 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 *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 *query = cl->get_query(cl);
const char *docroot = cl->srv->docroot;
int docroot_len = strlen(docroot);
char *pathptr = NULL;
bool slash;
int i = 0;
int len;
memset(&p, 0, sizeof(p));
path_phys[0] = 0;
path_info[0] = 0;
strcpy(buf, docroot);
strcat(buf, path);
/* create canon path */
len = strlen(buf);
slash = len && buf[len - 1] == '/';
len = min(len, sizeof(path_phys) - 1);
for (i = len; i >= 0; i--) {
char ch = buf[i];
bool exists;
if (ch != 0 && ch != '/')
continue;
buf[i] = 0;
exists = !!canonpath(buf, path_phys);
buf[i] = ch;
if (!exists)
continue;
/* test current path */
if (stat(path_phys, &p.stat))
continue;
snprintf(path_info, sizeof(path_info), "%s", buf + i);
break;
}
/* check whether found path is within docroot */
if (strncmp(path_phys, docroot, docroot_len) != 0 ||
(path_phys[docroot_len] != 0 &&
path_phys[docroot_len] != '/'))
return NULL;
/* is a regular file */
if (p.stat.st_mode & S_IFREG) {
p.root = docroot;
p.phys = path_phys;
p.name = &path_phys[docroot_len];
p.info = path_info[0] ? path_info : NULL;
return &p;
}
if (!(p.stat.st_mode & S_IFDIR))
return NULL;
if (path_info[0])
return NULL;
pathptr = path_phys + strlen(path_phys);
/* ensure trailing slash */
if (pathptr[-1] != '/') {
pathptr[0] = '/';
pathptr[1] = 0;
pathptr++;
}
/* if requested url resolves to a directory and a trailing slash
is missing in the request url, redirect the client to the same
url with trailing slash appended */
if (!slash) {
cl->redirect(cl, 302, "%s%s%s", &path_phys[docroot_len], query ? "?" : "", query ? query : "");
p.redirected = 1;
return &p;
}
/* try to locate index file */
len = path_phys + sizeof(path_phys) - pathptr - 1;
strcpy(pathptr, cl->srv->index_file);
if (stat(path_phys, &p.stat) < 0)
return NULL;
p.root = docroot;
p.phys = path_phys;
p.name = &path_phys[docroot_len];
return p.phys ? &p : NULL;
}
static char *file_unix2date(time_t ts, char *buf, int len)
{
struct tm *t = gmtime(&ts);
strftime(buf, len, "%a, %d %b %Y %H:%M:%S GMT", t);
return buf;
}
static const char * uh_file_mime_lookup(const char *path)
{
const struct mimetype *m = &uh_mime_types[0];
const char *e;
while (m->extn) {
e = &path[strlen(path)-1];
while (e >= path) {
if ((*e == '.' || *e == '/') && !strcasecmp(&e[1], m->extn))
return m->mime;
e--;
}
m++;
}
return "application/octet-stream";
}
static void uh_file_response_ok_hdrs(struct uh_client *cl, struct stat *s)
{
char buf[128];
cl->printf(cl, "Last-Modified: %s\r\n", file_unix2date(s->st_mtime, buf, sizeof(buf)));
cl->printf(cl, "Date: %s\r\n", file_unix2date(time(NULL), buf, sizeof(buf)));
}
static void uh_file_response_304(struct uh_client *cl, struct stat *s)
{
cl->send_header(cl, 304, "Not Modified", 0);
uh_file_response_ok_hdrs(cl, s);
}
static void uh_file_response_200(struct uh_client *cl, struct stat *s)
{
cl->send_header(cl, 200, "OK", s->st_size);
uh_file_response_ok_hdrs(cl, s);
}
static int uh_file_if_modified_since(struct uh_client *cl, struct stat *s)
{
const char *date = kvlist_get(&cl->request.header, "if-modified-since");
struct tm t;
if (!date)
return true;
memset(&t, 0, sizeof(t));
if ((strptime(date, "%a, %d %b %Y %H:%M:%S %Z", &t) ? timegm(&t) : 0) >= s->st_mtime) {
uh_file_response_304(cl, s);
return false;
}
return true;
}
static void file_write_cb(struct uh_client *cl)
{
static char buf[4096];
int fd = cl->dispatch.file.fd;
int r;
while (cl->us->w.data_bytes < 256) {
r = read(fd, buf, sizeof(buf));
if (r < 0) {
if (errno == EINTR)
continue;
uh_log_err("read");
}
if (r <= 0) {
cl->request_done(cl);
return;
}
cl->send(cl, buf, r);
}
}
static void uh_file_free(struct uh_client *cl)
{
close(cl->dispatch.file.fd);
}
static void uh_file_data(struct uh_client *cl, struct path_info *pi, int fd)
{
/* test preconditions */
if ((!uh_file_if_modified_since(cl, &pi->stat))) {
cl->printf(cl, "\r\n");
cl->request_done(cl);
close(fd);
return;
}
/* write status */
uh_file_response_200(cl, &pi->stat);
cl->printf(cl, "Content-Type: %s\r\n\r\n", uh_file_mime_lookup(pi->name));
/* send header */
if (cl->request.method == UH_HTTP_METHOD_HEAD) {
cl->request_done(cl);
close(fd);
return;
}
cl->state = CLIENT_STATE_DONE;
cl->dispatch.file.fd = fd;
cl->dispatch.write_cb = file_write_cb;
cl->dispatch.free = uh_file_free;
file_write_cb(cl);
}
static void uh_file_request(struct uh_client *cl, const char *path, struct path_info *pi)
{
int fd;
if (!(pi->stat.st_mode & S_IROTH))
goto error;
if (pi->stat.st_mode & S_IFREG) {
fd = open(pi->phys, O_RDONLY);
if (fd < 0)
goto error;
uh_file_data(cl, pi, fd);
return;
}
error:
cl->send_error(cl, 403, "Forbidden", "You don't have permission to access %s on this server.", path);
}
bool handle_file_request(struct uh_client *cl, const char *path)
{
struct path_info *pi;
pi = uh_path_lookup(cl, path);
if (!pi)
return false;
if (pi->redirected)
return true;
uh_file_request(cl, path, pi);
return true;
}

View File

@ -1,44 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#ifndef _FILE_H
#define _FILE_H
#include <sys/stat.h>
#include "client.h"
struct path_info {
const char *root;
const char *phys;
const char *name;
const char *info;
bool redirected;
struct stat stat;
};
struct mimetype {
const char *extn;
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

2498
src/http_parser.c 100644

File diff suppressed because it is too large Load Diff

439
src/http_parser.h 100644
View File

@ -0,0 +1,439 @@
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef http_parser_h
#define http_parser_h
#ifdef __cplusplus
extern "C" {
#endif
/* Also update SONAME in the Makefile whenever you change these. */
#define HTTP_PARSER_VERSION_MAJOR 2
#define HTTP_PARSER_VERSION_MINOR 9
#define HTTP_PARSER_VERSION_PATCH 2
#include <stddef.h>
#if defined(_WIN32) && !defined(__MINGW32__) && \
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
#include <BaseTsd.h>
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
* faster
*/
#ifndef HTTP_PARSER_STRICT
# define HTTP_PARSER_STRICT 1
#endif
/* Maximium header size allowed. If the macro is not defined
* before including this header then the default is used. To
* change the maximum header size, define the macro in the build
* environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
* the effective limit on the size of the header, define the macro
* to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
*/
#ifndef HTTP_MAX_HEADER_SIZE
# define HTTP_MAX_HEADER_SIZE (80*1024)
#endif
typedef struct http_parser http_parser;
typedef struct http_parser_settings http_parser_settings;
/* Callbacks should return non-zero to indicate an error. The parser will
* then halt execution.
*
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
* returning '1' from on_headers_complete will tell the parser that it
* should not expect a body. This is used when receiving a response to a
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
* chunked' headers that indicate the presence of a body.
*
* Returning `2` from on_headers_complete will tell parser that it should not
* expect neither a body nor any futher responses on this connection. This is
* useful for handling responses to a CONNECT request which may not contain
* `Upgrade` or `Connection: upgrade` headers.
*
* http_data_cb does not return data chunks. It will be called arbitrarily
* many times for each string. E.G. you might get 10 callbacks for "on_url"
* each providing just a few characters more data.
*/
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
typedef int (*http_cb) (http_parser*);
/* Status Codes */
#define HTTP_STATUS_MAP(XX) \
XX(100, CONTINUE, Continue) \
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
XX(102, PROCESSING, Processing) \
XX(200, OK, OK) \
XX(201, CREATED, Created) \
XX(202, ACCEPTED, Accepted) \
XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
XX(204, NO_CONTENT, No Content) \
XX(205, RESET_CONTENT, Reset Content) \
XX(206, PARTIAL_CONTENT, Partial Content) \
XX(207, MULTI_STATUS, Multi-Status) \
XX(208, ALREADY_REPORTED, Already Reported) \
XX(226, IM_USED, IM Used) \
XX(300, MULTIPLE_CHOICES, Multiple Choices) \
XX(301, MOVED_PERMANENTLY, Moved Permanently) \
XX(302, FOUND, Found) \
XX(303, SEE_OTHER, See Other) \
XX(304, NOT_MODIFIED, Not Modified) \
XX(305, USE_PROXY, Use Proxy) \
XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
XX(400, BAD_REQUEST, Bad Request) \
XX(401, UNAUTHORIZED, Unauthorized) \
XX(402, PAYMENT_REQUIRED, Payment Required) \
XX(403, FORBIDDEN, Forbidden) \
XX(404, NOT_FOUND, Not Found) \
XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
XX(406, NOT_ACCEPTABLE, Not Acceptable) \
XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
XX(408, REQUEST_TIMEOUT, Request Timeout) \
XX(409, CONFLICT, Conflict) \
XX(410, GONE, Gone) \
XX(411, LENGTH_REQUIRED, Length Required) \
XX(412, PRECONDITION_FAILED, Precondition Failed) \
XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
XX(414, URI_TOO_LONG, URI Too Long) \
XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
XX(417, EXPECTATION_FAILED, Expectation Failed) \
XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
XX(423, LOCKED, Locked) \
XX(424, FAILED_DEPENDENCY, Failed Dependency) \
XX(426, UPGRADE_REQUIRED, Upgrade Required) \
XX(428, PRECONDITION_REQUIRED, Precondition Required) \
XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
XX(501, NOT_IMPLEMENTED, Not Implemented) \
XX(502, BAD_GATEWAY, Bad Gateway) \
XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
XX(508, LOOP_DETECTED, Loop Detected) \
XX(510, NOT_EXTENDED, Not Extended) \
XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
enum http_status
{
#define XX(num, name, string) HTTP_STATUS_##name = num,
HTTP_STATUS_MAP(XX)
#undef XX
};
/* Request Methods */
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
/* pathological */ \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
/* WebDAV */ \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
/* subversion */ \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
/* upnp */ \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
/* RFC-5789 */ \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
/* CalDAV */ \
XX(30, MKCALENDAR, MKCALENDAR) \
/* RFC-2068, section 19.6.1.2 */ \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
/* icecast */ \
XX(33, SOURCE, SOURCE) \
enum http_method
{
#define XX(num, name, string) HTTP_##name = num,
HTTP_METHOD_MAP(XX)
#undef XX
};
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
/* Flag values for http_parser.flags field */
enum flags
{ F_CHUNKED = 1 << 0
, F_CONNECTION_KEEP_ALIVE = 1 << 1
, F_CONNECTION_CLOSE = 1 << 2
, F_CONNECTION_UPGRADE = 1 << 3
, F_TRAILING = 1 << 4
, F_UPGRADE = 1 << 5
, F_SKIPBODY = 1 << 6
, F_CONTENTLENGTH = 1 << 7
};
/* Map for errno-related constants
*
* The provided argument should be a macro that takes 2 arguments.
*/
#define HTTP_ERRNO_MAP(XX) \
/* No error */ \
XX(OK, "success") \
\
/* Callback-related errors */ \
XX(CB_message_begin, "the on_message_begin callback failed") \
XX(CB_url, "the on_url callback failed") \
XX(CB_header_field, "the on_header_field callback failed") \
XX(CB_header_value, "the on_header_value callback failed") \
XX(CB_headers_complete, "the on_headers_complete callback failed") \
XX(CB_body, "the on_body callback failed") \
XX(CB_message_complete, "the on_message_complete callback failed") \
XX(CB_status, "the on_status callback failed") \
XX(CB_chunk_header, "the on_chunk_header callback failed") \
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
\
/* Parsing-related errors */ \
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
XX(HEADER_OVERFLOW, \
"too many header bytes seen; overflow detected") \
XX(CLOSED_CONNECTION, \
"data received after completed connection: close message") \
XX(INVALID_VERSION, "invalid HTTP version") \
XX(INVALID_STATUS, "invalid HTTP status code") \
XX(INVALID_METHOD, "invalid HTTP method") \
XX(INVALID_URL, "invalid URL") \
XX(INVALID_HOST, "invalid host") \
XX(INVALID_PORT, "invalid port") \
XX(INVALID_PATH, "invalid path") \
XX(INVALID_QUERY_STRING, "invalid query string") \
XX(INVALID_FRAGMENT, "invalid fragment") \
XX(LF_EXPECTED, "LF character expected") \
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
XX(INVALID_CONTENT_LENGTH, \
"invalid character in content-length header") \
XX(UNEXPECTED_CONTENT_LENGTH, \
"unexpected content-length header") \
XX(INVALID_CHUNK_SIZE, \
"invalid character in chunk size header") \
XX(INVALID_CONSTANT, "invalid constant string") \
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
XX(STRICT, "strict mode assertion failed") \
XX(PAUSED, "parser is paused") \
XX(UNKNOWN, "an unknown error occurred")
/* Define HPE_* values for each errno value above */
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
enum http_errno {
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
};
#undef HTTP_ERRNO_GEN
/* Get an http_errno value from an http_parser */
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
struct http_parser {
/** PRIVATE **/
unsigned int type : 2; /* enum http_parser_type */
unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
unsigned int state : 7; /* enum state from http_parser.c */
unsigned int header_state : 7; /* enum header_state from http_parser.c */
unsigned int index : 7; /* index into current matcher */
unsigned int lenient_http_headers : 1;
uint32_t nread; /* # bytes read in various scenarios */
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
/** READ-ONLY **/
unsigned short http_major;
unsigned short http_minor;
unsigned int status_code : 16; /* responses only */
unsigned int method : 8; /* requests only */
unsigned int http_errno : 7;
/* 1 = Upgrade header was present and the parser has exited because of that.
* 0 = No upgrade header present.
* Should be checked when http_parser_execute() returns in addition to
* error checking.
*/
unsigned int upgrade : 1;
/** PUBLIC **/
void *data; /* A pointer to get hook to the "connection" or "socket" object */
};
struct http_parser_settings {
http_cb on_message_begin;
http_data_cb on_url;
http_data_cb on_status;
http_data_cb on_header_field;
http_data_cb on_header_value;
http_cb on_headers_complete;
http_data_cb on_body;
http_cb on_message_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
*/
http_cb on_chunk_header;
http_cb on_chunk_complete;
};
enum http_parser_url_fields
{ UF_SCHEMA = 0
, UF_HOST = 1
, UF_PORT = 2
, UF_PATH = 3
, UF_QUERY = 4
, UF_FRAGMENT = 5
, UF_USERINFO = 6
, UF_MAX = 7
};
/* Result structure for http_parser_parse_url().
*
* Callers should index into field_data[] with UF_* values iff field_set
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
* because we probably have padding left over), we convert any port to
* a uint16_t.
*/
struct http_parser_url {
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
uint16_t port; /* Converted UF_PORT string */
struct {
uint16_t off; /* Offset into buffer in which field starts */
uint16_t len; /* Length of run in buffer */
} field_data[UF_MAX];
};
/* Returns the library version. Bits 16-23 contain the major version number,
* bits 8-15 the minor version number and bits 0-7 the patch level.
* Usage example:
*
* unsigned long version = http_parser_version();
* unsigned major = (version >> 16) & 255;
* unsigned minor = (version >> 8) & 255;
* unsigned patch = version & 255;
* printf("http_parser v%u.%u.%u\n", major, minor, patch);
*/
unsigned long http_parser_version(void);
void http_parser_init(http_parser *parser, enum http_parser_type type);
/* Initialize http_parser_settings members to 0
*/
void http_parser_settings_init(http_parser_settings *settings);
/* Executes the parser. Returns number of parsed bytes. Sets
* `parser->http_errno` on error. */
size_t http_parser_execute(http_parser *parser,
const http_parser_settings *settings,
const char *data,
size_t len);
/* If http_should_keep_alive() in the on_headers_complete or
* on_message_complete callback returns 0, then this should be
* the last message on the connection.
* If you are the server, respond with the "Connection: close" header.
* If you are the client, close the connection.
*/
int http_should_keep_alive(const http_parser *parser);
/* Returns a string version of the HTTP method. */
const char *http_method_str(enum http_method m);
/* Returns a string version of the HTTP status code. */
const char *http_status_str(enum http_status s);
/* Return a string name of the given error */
const char *http_errno_name(enum http_errno err);
/* Return a string description of the given error */
const char *http_errno_description(enum http_errno err);
/* Initialize all http_parser_url members to 0 */
void http_parser_url_init(struct http_parser_url *u);
/* Parse a URL; return nonzero on failure */
int http_parser_parse_url(const char *buf, size_t buflen,
int is_connect,
struct http_parser_url *u);
/* Pause or un-pause the parser; a nonzero value pauses */
void http_parser_pause(http_parser *parser, int paused);
/* Checks if this is the final chunk of the body. */
int http_body_is_final(const http_parser *parser);
/* Change the maximum header size provided at compile time. */
void http_parser_set_max_header_size(uint32_t size);
#ifdef __cplusplus
}
#endif
#endif

142
src/log.c
View File

@ -1,45 +1,131 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
* MIT License
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* Copyright (c) 2019 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* 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
* Lesser General Public License for more details.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include "log.h"
void __uh_log(const char *filename, int line, int priority, const char *fmt, ...)
static int log_threshold = LOG_DEBUG;
static bool log_initialized;
static const char *ident;
void (*log_write)(int priority, const char *fmt, va_list ap);
static const char *log_ident()
{
va_list ap;
static char buf[128];
FILE *self;
static char line[64];
char *p = NULL;
char *sbuf;
snprintf(buf, sizeof(buf), "(%s:%d) ", filename, line);
va_start(ap, fmt);
vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), fmt, ap);
va_end(ap);
if (priority == LOG_ERR && errno > 0) {
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ":%s", strerror(errno));
errno = 0;
if ((self = fopen("/proc/self/status", "r")) != NULL) {
while (fgets(line, sizeof(line), self)) {
if (!strncmp(line, "Name:", 5)) {
strtok_r(line, "\t\n", &sbuf);
p = strtok_r(NULL, "\t\n", &sbuf);
break;
}
}
fclose(self);
}
ulog(priority, "%s\n", buf);
return p;
}
static inline void log_write_stdout(int priority, const char *fmt, va_list ap)
{
time_t now;
struct tm tm;
char buf[32];
now = time(NULL);
localtime_r(&now, &tm);
strftime(buf, sizeof(buf), "%Y/%m/%d %H:%M:%S", &tm);
fprintf(stderr, "%s ", buf);
vfprintf(stderr, fmt, ap);
}
static inline void log_write_syslog(int priority, const char *fmt, va_list ap)
{
vsyslog(priority, fmt, ap);
}
static inline void log_init()
{
if (log_initialized)
return;
ident = log_ident();
if (isatty(STDOUT_FILENO)) {
log_write = log_write_stdout;
} else {
log_write = log_write_syslog;
openlog(ident, 0, LOG_DAEMON);
}
log_initialized = true;
}
void uh_log_threshold(int threshold)
{
log_threshold = threshold;
}
void uh_log_close()
{
if (!log_initialized)
return;
closelog();
log_initialized = 0;
}
void __uh_log(const char *filename, int line, int priority, const char *fmt, ...)
{
static char new_fmt[256];
va_list ap;
if (priority > log_threshold)
return;
log_init();
snprintf(new_fmt, sizeof(new_fmt), "(%s:%d) %s", filename, line, fmt);
va_start(ap, fmt);
log_write(priority, new_fmt, ap);
va_end(ap);
}

View File

@ -1,33 +1,38 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
* MIT License
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* Copyright (c) 2019 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* 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
* Lesser General Public License for more details.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _LOG_H
#define _LOG_H
#ifndef _UH_LOG_H
#define _UH_LOG_H
#include <syslog.h>
#include <string.h>
#include <libubox/ulog.h>
void uh_log_threshold(int threshold);
void uh_log_close();
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
/*
* Use the syslog output log and include the name and number of rows at the call
*/
#define uh_log(priority, fmt...) __uh_log(__FILENAME__, __LINE__, priority, fmt)
#define uh_log_debug(fmt...) uh_log(LOG_DEBUG, fmt)

View File

@ -1,10 +0,0 @@
include_directories(${CMAKE_SOURCE_DIR}/src)
add_library(uhttpd-lua MODULE uhttpd-lua.c)
set_target_properties(uhttpd-lua PROPERTIES OUTPUT_NAME uhttpd PREFIX "")
target_link_libraries(uhttpd-lua uhttpd util)
install(TARGETS uhttpd-lua
LIBRARY DESTINATION lib/lua/${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}
)

View File

@ -1,582 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include <string.h>
#include "log.h"
#include "uhttpd.h"
#include "uhttpd-lua.h"
static const char *cli_registry = "libuhttpd-cli{obj}";
#if 0
static void lua_print_stack(lua_State *L, const char *info)
{
int i = 1;
printf("----------%s----------\n", info);
for (; i <= lua_gettop(L); i++) {
printf("%d %s\n", i, lua_typename(L, lua_type(L, i)));
}
}
#endif
static void *uh_create_userdata(lua_State *L, size_t size, const luaL_Reg *reg, const char *mt, lua_CFunction gc)
{
void *obj = lua_newuserdata(L, size);
memset(obj, 0, size);
luaL_newmetatable(L, mt);
/* metatable.__index = metatable */
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
if (gc) {
lua_pushcfunction(L, gc);
lua_setfield(L, -2, "__gc");
}
luaL_setfuncs(L, reg, 0);
lua_setmetatable(L, -2);
return obj;
}
static int lua_uh_send_header(lua_State *L)
{
struct lua_uh_client *lcl = luaL_checkudata(L, 1, LUA_UH_CLIENT_MT);
struct uh_client *cl = lcl->cl;
int code = lua_tointeger(L, 2);
const char *summary = lua_tostring(L, 3);
int len = lua_tointeger(L, 4);
cl->send_header(cl, code, summary, len);
return 0;
}
static int lua_uh_append_header(lua_State *L)
{
struct lua_uh_client *lcl = luaL_checkudata(L, 1, LUA_UH_CLIENT_MT);
struct uh_client *cl = lcl->cl;
const char *name = lua_tostring(L, 2);
const char *value = lua_tostring(L, 2);
cl->append_header(cl, name, value);
return 0;
}
static int lua_uh_header_end(lua_State *L)
{
struct lua_uh_client *lcl = luaL_checkudata(L, 1, LUA_UH_CLIENT_MT);
struct uh_client *cl = lcl->cl;
cl->header_end(cl);
return 0;
}
static int lua_uh_send(lua_State *L)
{
struct lua_uh_client *lcl = luaL_checkudata(L, 1, LUA_UH_CLIENT_MT);
struct uh_client *cl = lcl->cl;
const char *data;
size_t len;
data = lua_tolstring(L, 2, &len);
cl->send(cl, data, len);
return 0;
}
static int lua_uh_chunk_send(lua_State *L)
{
struct lua_uh_client *lcl = luaL_checkudata(L, 1, LUA_UH_CLIENT_MT);
struct uh_client *cl = lcl->cl;
const char *data;
size_t len;
data = lua_tolstring(L, 2, &len);
cl->chunk_send(cl, data, len);
return 0;
}
static int lua_uh_send_error(lua_State *L)
{
struct lua_uh_client *lcl = luaL_checkudata(L, 1, LUA_UH_CLIENT_MT);
struct uh_client *cl = lcl->cl;
int code = lua_tointeger(L, 2);
const char *summary = lua_tostring(L, 3);
const char *msg = lua_tostring(L, 4);
cl->send_error(cl, code, summary, msg);
return 0;
}
static int lua_uh_redirect(lua_State *L)
{
struct lua_uh_client *lcl = luaL_checkudata(L, 1, LUA_UH_CLIENT_MT);
struct uh_client *cl = lcl->cl;
int code = lua_tointeger(L, 2);
const char *url = lua_tostring(L, 3);
cl->redirect(cl, code, url);
return 0;
}
static int lua_uh_request_done(lua_State *L)
{
struct lua_uh_client *lcl = luaL_checkudata(L, 1, LUA_UH_CLIENT_MT);
struct uh_client *cl = lcl->cl;
cl->request_done(cl);
return 0;
}
static int lua_uh_get_http_method(lua_State *L)
{
struct lua_uh_client *lcl = luaL_checkudata(L, 1, LUA_UH_CLIENT_MT);
struct uh_client *cl = lcl->cl;
lua_pushinteger(L, cl->request.method);
return 1;
}
static int lua_uh_get_http_version(lua_State *L)
{
struct lua_uh_client *lcl = luaL_checkudata(L, 1, LUA_UH_CLIENT_MT);
struct uh_client *cl = lcl->cl;
lua_pushinteger(L, cl->request.version);
return 1;
}
static int lua_uh_get_remote_addr(lua_State *L)
{
struct lua_uh_client *lcl = luaL_checkudata(L, 1, LUA_UH_CLIENT_MT);
struct uh_client *cl = lcl->cl;
const char *addr = cl->get_peer_addr(cl);
if (addr)
lua_pushstring(L, addr);
else
lua_pushnil(L);
return 1;
}
static int lua_uh_get_header(lua_State *L)
{
struct lua_uh_client *lcl = luaL_checkudata(L, 1, LUA_UH_CLIENT_MT);
struct uh_client *cl = lcl->cl;
const char *name = lua_tostring(L, 2);
const char *value;
if (name) {
value = cl->get_header(cl, name);
if (value)
lua_pushstring(L, value);
else
lua_pushnil(L);
return 1;
}
lua_newtable(L);
kvlist_for_each(&cl->request.header, name, value) {
lua_pushstring(L, value);
lua_setfield(L, -2, name);
}
return 1;
}
static int lua_uh_get_var(lua_State *L)
{
struct lua_uh_client *lcl = luaL_checkudata(L, 1, LUA_UH_CLIENT_MT);
struct uh_client *cl = lcl->cl;
const char *name = lua_tostring(L, 2);
const char *value;
if (name) {
value = cl->get_var(cl, name);
if (value)
lua_pushstring(L, value);
else
lua_pushnil(L);
return 1;
}
lua_newtable(L);
kvlist_for_each(&cl->request.var, name, value) {
lua_pushstring(L, value);
lua_setfield(L, -2, name);
}
return 1;
}
static int lua_uh_get_query(lua_State *L)
{
struct lua_uh_client *lcl = luaL_checkudata(L, 1, LUA_UH_CLIENT_MT);
struct uh_client *cl = lcl->cl;
const char *query = cl->get_query(cl);
if (query)
lua_pushstring(L, query);
else
lua_pushnil(L);
return 1;
}
static int lua_uh_get_url(lua_State *L)
{
struct lua_uh_client *lcl = luaL_checkudata(L, 1, LUA_UH_CLIENT_MT);
struct uh_client *cl = lcl->cl;
const char *url = cl->get_url(cl);
if (url)
lua_pushstring(L, url);
else
lua_pushnil(L);
return 1;
}
static int lua_uh_get_body(lua_State *L)
{
struct lua_uh_client *lcl = luaL_checkudata(L, 1, LUA_UH_CLIENT_MT);
struct uh_client *cl = lcl->cl;
const char *body;
int len;
body = cl->get_body(cl, &len);
if (body)
lua_pushlstring(L, body, len);
else
lua_pushnil(L);
return 1;
}
static const luaL_Reg client_reg[] = {
{"send_header", lua_uh_send_header},
{"append_header", lua_uh_append_header},
{"header_end", lua_uh_header_end},
{"send", lua_uh_send},
{"chunk_send", lua_uh_chunk_send},
{"send_error", lua_uh_send_error},
{"redirect", lua_uh_redirect},
{"request_done", lua_uh_request_done},
{"get_http_method", lua_uh_get_http_method},
{"get_http_version", lua_uh_get_http_version},
{"get_remote_addr", lua_uh_get_remote_addr},
{"get_header", lua_uh_get_header},
{"get_var", lua_uh_get_var},
{"get_query", lua_uh_get_query},
{"get_url", lua_uh_get_url},
{"get_body", lua_uh_get_body},
{ NULL, NULL }
};
static void lua_on_accept(struct uh_client *cl)
{
lua_State *L = cl->srv->L;
struct lua_uh_client *lcl;
lua_pushlightuserdata(L, &cli_registry);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_pushlightuserdata(L, cl);
lcl = uh_create_userdata(L, sizeof(struct lua_uh_client), client_reg, LUA_UH_CLIENT_MT, NULL);
lcl->cl = cl;
lua_rawset(L, -3);
lua_pop(L, 1);
}
static int lua_do_request_cb(lua_State *L, struct uh_client *cl)
{
const char *path = cl->get_path(cl);
lua_pushlightuserdata(L, &cli_registry);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_pushlightuserdata(L, cl);
lua_rawget(L, -2);
lua_insert(L, -2);
lua_pop(L, 1);
lua_pushstring(L, path);
lua_call(L, 2, 1);
return lua_tointeger(L, -1);
}
static int lua_on_request(struct uh_client *cl)
{
struct lua_uh_server *lsrv = container_of(cl->srv, struct lua_uh_server, srv);
lua_State *L = cl->srv->L;
lua_getglobal(L, "__uh_on_request");
lua_rawgeti(L, -1, lsrv->request_ref);
lua_remove(L, -2);
return lua_do_request_cb(L, cl);
}
static void lua_on_error404(struct uh_client *cl)
{
struct lua_uh_server *lsrv = container_of(cl->srv, struct lua_uh_server, srv);
lua_State *L = cl->srv->L;
lua_getglobal(L, "__uh_on_error404");
lua_rawgeti(L, -1, lsrv->error404_ref);
lua_remove(L, -2);
lua_do_request_cb(L, cl);
}
static int lua_uh_ssl_init(lua_State *L)
{
#if (!UHTTPD_SSL_SUPPORT)
lua_pushstring(L, "SSL is not compiled in");
lua_error(L);
#else
struct lua_uh_server *lsrv = luaL_checkudata(L, 1, LUA_UH_SERVER_MT);
const char *cert = luaL_checkstring(L, 2);
const char *key = luaL_checkstring(L, 3);
if (lsrv->srv.ssl_init(&lsrv->srv, key, cert) < 0)
luaL_error(L, "SSL init failed");
#endif
return 0;
}
static int lua_uh_set_options(lua_State *L)
{
struct lua_uh_server *lsrv = luaL_checkudata(L, 1, LUA_UH_SERVER_MT);
struct uh_server *srv = &lsrv->srv;
luaL_checktype(L, 2, LUA_TTABLE);
lua_getfield(L, 2, "docroot");
if (lua_tostring(L, -1))
srv->set_docroot(srv, lua_tostring(L, -1));
lua_pop(L, 1);
lua_getfield(L, 2, "index");
if (lua_tostring(L, -1))
srv->set_index_file(srv, lua_tostring(L, -1));
lua_pop(L, 1);
return 0;
}
static int lua_uh_set_error404_cb(lua_State *L)
{
struct lua_uh_server *lsrv = luaL_checkudata(L, 1, LUA_UH_SERVER_MT);
luaL_checktype(L, 2, LUA_TFUNCTION);
lua_getglobal(L, "__uh_on_error404");
lua_pushvalue(L, -2);
lsrv->error404_ref = luaL_ref(L, -2);
lua_pop(L, 1);
lsrv->srv.on_error404 = lua_on_error404;
return 0;
}
static int lua_uh_set_request_cb(lua_State *L)
{
struct lua_uh_server *lsrv = luaL_checkudata(L, 1, LUA_UH_SERVER_MT);
luaL_checktype(L, 2, LUA_TFUNCTION);
lua_getglobal(L, "__uh_on_request");
lua_pushvalue(L, -2);
lsrv->request_ref = luaL_ref(L, -2);
lua_pop(L, 1);
lsrv->srv.on_request = lua_on_request;
return 0;
}
static int lua_uh_server_free(lua_State *L)
{
struct lua_uh_server *lsrv = luaL_checkudata(L, 1, LUA_UH_SERVER_MT);
lsrv->srv.free(&lsrv->srv);
return 0;
}
static const luaL_Reg server_reg[] = {
{ "ssl_init", lua_uh_ssl_init },
{ "set_options", lua_uh_set_options },
{ "on_error404", lua_uh_set_error404_cb },
{ "on_request", lua_uh_set_request_cb },
{ "free", lua_uh_server_free },
{ NULL, NULL }
};
static void lua_on_client_free(struct uh_client *cl)
{
lua_State *L = cl->srv->L;
lua_pushlightuserdata(L, &cli_registry);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_pushlightuserdata(L, cl);
lua_pushnil(L);
lua_rawset(L, -3);
lua_pop(L, 1);
}
static int lua_uh_new(lua_State *L)
{
int port = lua_tointeger(L, -1);
const char *host = lua_tostring(L, -2);
struct lua_uh_server *lsrv;
int sock;
sock = uh_server_open(host, port);
if (!sock) {
lua_pushnil(L);
lua_pushstring(L, "Bind sock failed");
return 2;
}
lsrv = uh_create_userdata(L, sizeof(struct lua_uh_server), server_reg, LUA_UH_SERVER_MT, lua_uh_server_free);
uh_server_init(&lsrv->srv, sock);
lsrv->srv.L = L;
lsrv->srv.on_accept = lua_on_accept;
lsrv->srv.on_client_free = lua_on_client_free;
return 1;
}
static int lua_uh_log(lua_State *L)
{
int priority = lua_tointeger(L, 1);
const char *msg = lua_tostring(L, 2);
luaL_where(L, 1);
ulog(priority, "%s%s\n", lua_tostring(L, -1), msg);
return 0;
}
static int lua_uh_set_log_threshold(lua_State *L)
{
ulog_threshold(lua_tointeger(L, 1));
return 0;
}
static const luaL_Reg uhttpd_fun[] = {
{"new", lua_uh_new},
{"log", lua_uh_log},
{"set_log_threshold", lua_uh_set_log_threshold},
{NULL, NULL}
};
int luaopen_uhttpd(lua_State *L)
{
/**
* Create a "registry" of light userdata pointers into the
* fulluserdata so that we can get handles into the lua objects.
*/
lua_pushlightuserdata(L, &cli_registry);
lua_newtable(L);
lua_rawset(L, LUA_REGISTRYINDEX);
lua_newtable(L);
lua_setglobal(L, "__uh_on_request");
lua_newtable(L);
lua_setglobal(L, "__uh_on_error404");
lua_newtable(L);
luaL_setfuncs(L, uhttpd_fun, 0);
lua_pushstring(L, UHTTPD_VERSION_STRING);
lua_setfield(L, -2, "VERSION");
#if (UHTTPD_SSL_SUPPORT)
lua_pushboolean(L, 1);
#else
lua_pushboolean(L, 0);
#endif
lua_setfield(L, -2, "SSL_SUPPORTED");
lua_pushinteger(L, LOG_DEBUG);
lua_setfield(L, -2, "LOG_DEBUG");
lua_pushinteger(L, LOG_INFO);
lua_setfield(L, -2, "LOG_INFO");
lua_pushinteger(L, LOG_ERR);
lua_setfield(L, -2, "LOG_ERR");
lua_pushinteger(L, UH_REQUEST_DONE);
lua_setfield(L, -2, "REQUEST_DONE");
lua_pushinteger(L, UH_REQUEST_CONTINUE);
lua_setfield(L, -2, "REQUEST_CONTINUE");
lua_pushinteger(L, UH_HTTP_VER_09);
lua_setfield(L, -2, "HTTP_VER_09");
lua_pushinteger(L, UH_HTTP_VER_10);
lua_setfield(L, -2, "HTTP_VER_10");
lua_pushinteger(L, UH_HTTP_VER_11);
lua_setfield(L, -2, "HTTP_VER_11");
lua_pushinteger(L, UH_HTTP_METHOD_GET);
lua_setfield(L, -2, "HTTP_METHOD_GET");
lua_pushinteger(L, UH_HTTP_METHOD_POST);
lua_setfield(L, -2, "HTTP_METHOD_POST");
lua_pushinteger(L, UH_HTTP_METHOD_HEAD);
lua_setfield(L, -2, "HTTP_METHOD_HEAD");
return 1;
}

View File

@ -1,47 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#ifndef _UHTTPD_LUA_H
#define _UHTTPD_LUA_H
#include <lauxlib.h>
#include <lualib.h>
/* Compatibility defines */
#if LUA_VERSION_NUM <= 501
/* NOTE: this only works if nups == 0! */
#define luaL_setfuncs(L, fns, nups) luaL_register((L), NULL, (fns))
#endif
#define LUA_UH_SERVER_MT "libuhttpd"
#define LUA_UH_CLIENT_MT "libuhttpd(cli)"
struct lua_uh_client {
struct uh_client *cl;
};
struct lua_uh_server {
struct uh_server srv;
int error404_ref;
int request_ref;
};
#endif

View File

@ -1,653 +0,0 @@
/*
* Based on code found at https://github.com/openwrt/luci/blob/master/modules/luci-base/src/template_parser.c
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include <stdlib.h>
#include <stdint.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#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, "&#34;", 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 = lua_touserdata(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->on_error404) {
cl->srv->on_error404(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_pushlightuserdata(L, 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, "<h2><b>Lua Error</b></h2>\n%s\n", lua_tostring(L, -1));
cl->chunk_printf(cl, "</body></html>\n");
lua_pop(L, -1);
}
cl->request_done(cl);
return;
err:
cl->send_error(cl, 500, "Internal Server Error", NULL);
}

267
src/ssl.c 100644
View File

@ -0,0 +1,267 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <unistd.h>
#include <errno.h>
#include "ssl.h"
#if UHTTPD_SSL_SUPPORT
void *uh_ssl_ctx_init(const char *cert, const char *key)
{
#if UHTTPD_HAVE_MBEDTLS
struct mbedtls_ctx *ctx;
ctx = calloc(1, sizeof(struct mbedtls_ctx));
if (!ctx) {
uh_log_err("calloc: %s\n", strerror(errno));
return NULL;
}
#if defined(MBEDTLS_SSL_CACHE_C)
mbedtls_ssl_cache_init(&ctx->cache);
#endif
mbedtls_entropy_init(&ctx->entropy);
mbedtls_ctr_drbg_init(&ctx->ctr_drbg);
if (mbedtls_x509_crt_parse_file(&ctx->cert, cert) != 0) {
uh_log_err("Invalid SSL cert\n");
goto err;
}
if (mbedtls_pk_parse_keyfile(&ctx->pkey, key, NULL) != 0) {
uh_log_err("Invalid SSL key\n");
goto err;
}
if(mbedtls_ssl_config_defaults(&ctx->conf, MBEDTLS_SSL_IS_SERVER,
MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
uh_log_err("Failed to init SSL config\n");
goto err;
}
mbedtls_ctr_drbg_seed(&ctx->ctr_drbg, mbedtls_entropy_func, &ctx->entropy, NULL, 0);
mbedtls_ssl_conf_rng(&ctx->conf, mbedtls_ctr_drbg_random, &ctx->ctr_drbg);
#if defined(MBEDTLS_SSL_CACHE_C)
mbedtls_ssl_conf_session_cache(&ctx->conf, &ctx->cache,
mbedtls_ssl_cache_get, mbedtls_ssl_cache_set);
#endif
mbedtls_ssl_conf_ca_chain(&ctx->conf, ctx->cert.next, NULL);
if (mbedtls_ssl_conf_own_cert(&ctx->conf, &ctx->cert, &ctx->pkey) != 0) {
uh_log_err("Private key does not match the certificate public key\n");
goto err;
}
#else
SSL_CTX *ctx;
#if UHTTPD_HAVE_WOLFSSL
wolfSSL_Init();
ctx = SSL_CTX_new(TLSv1_2_server_method());
#elif OPENSSL_VERSION_NUMBER < 0x10100000L
SSL_library_init();
SSL_load_error_strings();
ctx = SSL_CTX_new(SSLv23_server_method());
#else
OPENSSL_init_ssl(0, NULL);
ctx = SSL_CTX_new(TLS_server_method());
#endif
if (SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM) != 1) {
uh_log_err("Invalid SSL cert\n");
goto err;
}
if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) != 1) {
uh_log_err("Invalid SSL key\n");;
goto err;
}
if (!SSL_CTX_check_private_key(ctx)) {
uh_log_err("Private key does not match the certificate public key\n");
goto err;
}
#endif
return ctx;
err:
#if UHTTPD_HAVE_MBEDTLS
free(ctx);
#else
SSL_CTX_free(ctx);
#endif
return NULL;
}
void uh_ssl_ctx_free(void *ctx)
{
if (!ctx)
return;
#if UHTTPD_HAVE_MBEDTLS
struct mbedtls_ctx *mctx = (struct mbedtls_ctx *)ctx;
mbedtls_x509_crt_free(&mctx->cert);
mbedtls_pk_free(&mctx->pkey);
mbedtls_ssl_config_free(&mctx->conf);
#if defined(MBEDTLS_SSL_CACHE_C)
mbedtls_ssl_cache_free(&mctx->cache);
#endif
mbedtls_ctr_drbg_free(&mctx->ctr_drbg);
mbedtls_entropy_free(&mctx->entropy);
#else
SSL_CTX_free(ctx);
#endif
}
void uh_ssl_free(void *ssl)
{
if (!ssl)
return;
#if UHTTPD_HAVE_MBEDTLS
mbedtls_ssl_free(ssl);
#else
SSL_shutdown(ssl);
SSL_free(ssl);
#endif
}
#if UHTTPD_HAVE_MBEDTLS
static const char *mbedtls_err_string(int err)
{
static char error_buf[200];
mbedtls_strerror(err, error_buf, 200);
return error_buf;
}
static int mbedtls_net_send(void *ctx, const unsigned char *buf, size_t len)
{
int sock = (long long)ctx;
int n = write(sock, buf, len);
if (n >= 0)
return n;
return ((errno == EAGAIN || errno == EINPROGRESS) ? MBEDTLS_ERR_SSL_WANT_WRITE : -1);
}
static int mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len)
{
int sock = (long long)ctx;
int n = read(sock, buf, len);
if (n >= 0)
return n;
return ((errno == EAGAIN || errno == EINPROGRESS) ? MBEDTLS_ERR_SSL_WANT_READ : -1);
}
#endif
void *uh_ssl_new(void *ctx, int sock)
{
#if UHTTPD_HAVE_MBEDTLS
struct mbedtls_ctx *mctx = (struct mbedtls_ctx *)ctx;
mbedtls_ssl_context *ssl = calloc(1, sizeof(mbedtls_ssl_context));
mbedtls_ssl_setup(ssl, &mctx->conf);
mbedtls_ssl_set_bio(ssl, (void *)(long long)sock, mbedtls_net_send, mbedtls_net_recv, NULL);
#else
SSL *ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);
#endif
return ssl;
}
int uh_ssl_handshake(void *ssl)
{
#if UHTTPD_HAVE_MBEDTLS
int ret = mbedtls_ssl_handshake(ssl);
if (ret != 0) {
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)
return UH_SSL_ERROR_AGAIN;
uh_log_err("SSL handshake failed: %s\n", mbedtls_err_string(ret));
return UH_SSL_ERROR_UNKNOWN;
}
#else
int ret = SSL_accept(ssl);
if (ret != 1) {
int err = SSL_get_error(ssl, ret);
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
return UH_SSL_ERROR_AGAIN;
uh_log_err("SSL handshake failed: %s\n", ERR_reason_error_string(err));
return UH_SSL_ERROR_UNKNOWN;
}
#endif
return UH_SSL_ERROR_NONE;
}
int uh_ssl_read(void *ssl, void *buf, size_t count)
{
#if UHTTPD_HAVE_MBEDTLS
int ret = mbedtls_ssl_read(ssl, buf, count);
if (ret < 0) {
if (ret == MBEDTLS_ERR_SSL_WANT_READ)
return UH_SSL_ERROR_AGAIN;
if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)
return 0;
uh_log_err("mbedtls_ssl_read: %s\n", mbedtls_err_string(ret));
return UH_SSL_ERROR_UNKNOWN;
}
#else
int ret = SSL_read(ssl, buf, count);
if (ret < 0) {
int err = SSL_get_error(ssl, ret);
if (err == SSL_ERROR_WANT_READ)
return UH_SSL_ERROR_AGAIN;
uh_log_err("SSL_read: %s\n", ERR_reason_error_string(err));
return UH_SSL_ERROR_UNKNOWN;
}
#endif
return ret;
}
int uh_ssl_write(void *ssl, void *buf, size_t count)
{
#if UHTTPD_HAVE_MBEDTLS
int ret = mbedtls_ssl_write(ssl, buf, count);
if (ret < 0) {
if (ret == MBEDTLS_ERR_SSL_WANT_WRITE)
return UH_SSL_ERROR_AGAIN;
uh_log_err("mbedtls_ssl_write: %s\n", mbedtls_err_string(ret));
return UH_SSL_ERROR_UNKNOWN;
}
#else
int ret = SSL_write(ssl, buf, count);
if (ret < 0) {
int err = SSL_get_error(ssl, ret);
if (err == SSL_ERROR_WANT_WRITE)
return UH_SSL_ERROR_AGAIN;
uh_log_err("SSL_write: %s\n", ERR_reason_error_string(err));
return UH_SSL_ERROR_UNKNOWN;
}
#endif
return ret;
}
#endif

82
src/ssl.h 100644
View File

@ -0,0 +1,82 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _UH_SSL_H
#define _UH_SSL_H
#include "config.h"
#include "log.h"
#if UHTTPD_SSL_SUPPORT
#if UHTTPD_HAVE_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#elif UHTTPD_HAVE_WOLFSSL
#define WC_NO_HARDEN
#include <wolfssl/openssl/ssl.h>
#include <wolfssl/openssl/err.h>
#else
#include <mbedtls/debug.h>
#include <mbedtls/ecp.h>
#include <mbedtls/platform.h>
#include <mbedtls/ssl.h>
#include <mbedtls/x509_crt.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/entropy.h>
#include <mbedtls/error.h>
#if defined(MBEDTLS_SSL_CACHE_C)
#include <mbedtls/ssl_cache.h>
#endif
struct mbedtls_ctx {
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ssl_config conf;
mbedtls_x509_crt cert;
mbedtls_pk_context pkey;
#if defined(MBEDTLS_SSL_CACHE_C)
mbedtls_ssl_cache_context cache;
#endif
};
#endif
#define UH_SSL_ERROR_NONE 0
#define UH_SSL_ERROR_AGAIN -1
#define UH_SSL_ERROR_UNKNOWN -2
void *uh_ssl_ctx_init(const char *cert, const char *key);
void uh_ssl_ctx_free(void *ctx);
void uh_ssl_free(void *ssl);
void *uh_ssl_new(void *ctx, int sock);
int uh_ssl_handshake(void *ssl);
int uh_ssl_read(void *ssl, void *buf, size_t count);
int uh_ssl_write(void *ssl, void *buf, size_t count);
#endif
#endif

View File

@ -1,110 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include <dlfcn.h>
#include <errno.h>
#include "uhttpd.h"
#include "uh_ssl.h"
#include "log.h"
static bool _init = false;
static struct ustream_ssl_ops *ops;
static void *dlh;
static void *ctx;
int uh_ssl_init(struct uh_server *srv, const char *key, const char *crt)
{
srv->ssl = true;
if (_init)
return 0;
dlh = dlopen("libustream-ssl.so", RTLD_LAZY | RTLD_LOCAL);
if (!dlh) {
uh_log_err("Failed to load ustream-ssl library: %s", dlerror());
return -ENOENT;
}
ops = dlsym(dlh, "ustream_ssl_ops");
if (!ops) {
uh_log_err("Could not find required symbol 'ustream_ssl_ops' in ustream-ssl library");
return -ENOENT;
}
ctx = ops->context_new(true);
if (!ctx) {
uh_log_err("Failed to initialize ustream-ssl");
return -EINVAL;
}
if (ops->context_set_crt_file(ctx, crt) ||
ops->context_set_key_file(ctx, key)) {
uh_log_err("Failed to load certificate/key files");
return -EINVAL;
}
_init = true;
return 0;
}
void uh_ssl_free()
{
if (_init) {
_init = false;
ops->context_free(ctx);
}
}
static void ssl_ustream_read_cb(struct ustream *s, int bytes)
{
struct uh_client *cl = container_of(s, struct uh_client, ssl.stream);
uh_client_read_cb(cl);
}
static void ssl_ustream_write_cb(struct ustream *s, int bytes)
{
struct uh_client *cl = container_of(s, struct uh_client, ssl.stream);
if (cl->dispatch.write_cb)
cl->dispatch.write_cb(cl);
}
static void ssl_notify_state(struct ustream *s)
{
struct uh_client *cl = container_of(s, struct uh_client, ssl.stream);
uh_client_notify_state(cl);
}
void uh_ssl_client_attach(struct uh_client *cl)
{
cl->us = &cl->ssl.stream;
ops->init(&cl->ssl, &cl->sfd.stream, ctx, true);
cl->us->notify_read = ssl_ustream_read_cb;
cl->us->notify_write = ssl_ustream_write_cb;
cl->us->notify_state = ssl_notify_state;
}
void uh_ssl_client_detach(struct uh_client *cl)
{
ustream_free(&cl->ssl.stream);
}

View File

@ -1,53 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#ifndef __UHTTPD_SSL_H
#define __UHTTPD_SSL_H
#include "config.h"
#if (UHTTPD_SSL_SUPPORT)
int uh_ssl_init(struct uh_server *srv, const char *key, const char *crt);
void uh_ssl_free();
void uh_ssl_client_attach(struct uh_client *cl);
void uh_ssl_client_detach(struct uh_client *cl);
#else
static inline int uh_ssl_init(const char *key, const char *crt)
{
return -1;
}
static inline void uh_ssl_free()
{
}
static inline void uh_ssl_client_attach(struct uh_client *cl)
{
}
static inline void uh_ssl_client_detach(struct uh_client *cl)
{
}
#endif
#endif

View File

@ -1,118 +1,158 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
* MIT License
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* Copyright (c) 2019 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* 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
* Lesser General Public License for more details.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libubox/usock.h>
#include <libubox/avl-cmp.h>
#include <errno.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include "uhttpd.h"
#include "uh_ssl.h"
#include "utils.h"
#include "ssl.h"
#include "log.h"
static void uh_set_docroot(struct uh_server *srv, const char *docroot)
{
free(srv->docroot);
srv->docroot = strdup(docroot);
}
static void uh_set_index_file(struct uh_server *srv, const char *index_file)
{
free(srv->index_file);
srv->index_file = strdup(index_file);
}
static void uh_server_free(struct uh_server *srv)
{
struct uh_client *cl, *tmp;
struct uh_connection *conn = srv->conns;
if (srv) {
close(srv->fd.fd);
uloop_fd_delete(&srv->fd);
ev_io_stop(srv->loop, &srv->ior);
list_for_each_entry_safe(cl, tmp, &srv->clients, list)
cl->free(cl);
if (srv->sock > 0)
close(srv->sock);
uh_ssl_free();
free(srv->docroot);
free(srv->index_file);
}
}
static void uh_accept_cb(struct uloop_fd *fd, unsigned int events)
{
struct uh_server *srv = container_of(fd, struct uh_server, fd);
uh_accept_client(srv, srv->ssl);
}
int uh_server_open(const char *host, int port)
{
int sock = usock(USOCK_TCP | USOCK_SERVER | USOCK_IPV4ONLY, host, usock_port(port));
if (sock < 0) {
uh_log_err("usock");
return sock;
while (conn) {
struct uh_connection *next = conn->next;
conn->free(conn);
conn = next;
}
return sock;
}
void uh_server_init(struct uh_server *srv, int sock)
{
srv->docroot = strdup(".");
srv->index_file = strdup("index.html");
srv->fd.fd = sock;
srv->fd.cb = uh_accept_cb;
uloop_fd_add(&srv->fd, ULOOP_READ);
INIT_LIST_HEAD(&srv->clients);
srv->free = uh_server_free;
srv->set_docroot = uh_set_docroot;
srv->set_index_file = uh_set_index_file;
#if (UHTTPD_SSL_SUPPORT)
srv->ssl_init = uh_ssl_init;
#if UHTTPD_SSL_SUPPORT
uh_ssl_ctx_free(srv->ssl_ctx);
#endif
}
struct uh_server *uh_server_new(const char *host, int port)
static void uh_accept_cb(struct ev_loop *loop, struct ev_io *w, int revents)
{
struct uh_server *srv;
struct uh_server *srv = container_of(w, struct uh_server, ior);
struct uh_connection *conn;
struct sockaddr_in addr;
socklen_t addr_len = sizeof(addr);
int sock;
sock = uh_server_open(host, port);
if (sock < 0)
return NULL;
srv = calloc(1, sizeof(struct uh_server));
if (!srv) {
uh_log_err("calloc");
goto err;
sock = accept4(srv->sock, (struct sockaddr *)&addr, &addr_len, SOCK_NONBLOCK);
if (sock < 0) {
uh_log_err("accept: %s\n", strerror(errno));
return;
}
uh_server_init(srv, sock);
uh_log_debug("New connection: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
conn = uh_new_connection(srv, sock, &addr);
if (!conn)
return;
if (!srv->conns) {
srv->conns = conn;
return;
}
conn->next = srv->conns;
srv->conns->prev = conn;
srv->conns = conn;
}
struct uh_server *uh_server_new(struct ev_loop *loop, const char *host, int port)
{
struct uh_server *srv;
srv = malloc(sizeof(struct uh_server));
if (!srv) {
uh_log_err("malloc: %s\n", strerror(errno));
return NULL;
}
if (uh_server_init(srv, loop, host, port) < 0) {
free(srv);
return NULL;
}
return srv;
err:
close(sock);
return NULL;
}
#if UHTTPD_SSL_SUPPORT
static int uh_server_ssl_init(struct uh_server *srv, const char *cert, const char *key)
{
srv->ssl_ctx = uh_ssl_ctx_init(cert, key);
return srv->ssl_ctx ? 0 : -1;
}
#endif
int uh_server_init(struct uh_server *srv, struct ev_loop *loop, const char *host, int port)
{
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_ANY),
.sin_port = htons(port)
};
int sock = -1;
int opt = 1;
memset(srv, 0, sizeof(struct uh_server));
if (host)
addr.sin_addr.s_addr = inet_addr(host);
sock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (sock < 0) {
uh_log_err("socket: %s\n", strerror(errno));
return -1;
}
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) <0) {
close(sock);
uh_log_err("bind: %s\n", strerror(errno));
return -1;
}
listen(sock, SOMAXCONN);
srv->loop = loop ? loop : EV_DEFAULT;
srv->sock = sock;
srv->free = uh_server_free;
#if UHTTPD_SSL_SUPPORT
srv->ssl_init = uh_server_ssl_init;
#endif
ev_io_init(&srv->ior, uh_accept_cb, sock, EV_READ);
ev_io_start(srv->loop, &srv->ior);
return 0;
}

View File

@ -1,59 +1,58 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
* MIT License
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* Copyright (c) 2019 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* 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
* Lesser General Public License for more details.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _UHTTPD_H
#define _UHTTPD_H
#include <ev.h>
#include "connection.h"
#include "config.h"
#include "client.h"
#include "log.h"
struct uh_server {
bool ssl;
struct uloop_fd fd;
char *docroot;
char *index_file;
int nclients;
struct list_head clients;
int sock;
struct ev_loop *loop;
struct ev_io ior;
struct uh_connection *conns;
void (*free)(struct uh_server *srv);
void (*set_docroot)(struct uh_server *srv, const char *docroot);
void (*set_index_file)(struct uh_server *srv, const char *index_file);
void (*on_error404)(struct uh_client *cl);
int (*on_request)(struct uh_client *cl);
void (*on_accept)(struct uh_client *cl);
void (*on_client_free)(struct uh_client *cl);
#if (UHTTPD_SSL_SUPPORT)
int (*ssl_init)(struct uh_server *srv, const char *key, const char *crt);
#endif
#if (UHTTPD_LUA_SUPPORT)
void *L;
void (*on_request)(struct uh_connection *conn);
#if UHTTPD_SSL_SUPPORT
void *ssl_ctx;
int (*ssl_init)(struct uh_server *srv, const char *cert, const char *key);
#endif
};
struct uh_server *uh_server_new(const char *host, int port);
/*
* uh_server_new - creat an uh_server struct and init it
* @loop: If NULL will use EV_DEFAULT
* @host: If NULL will listen on "0.0.0.0"
* @port: port to listen on
*/
struct uh_server *uh_server_new(struct ev_loop *loop, const char *host, int port);
int uh_server_open(const char *host, int port);
void uh_server_init(struct uh_server *srv, int sock);
#if (UHTTPD_LUA_SUPPORT)
void uh_template(struct uh_client *cl);
#endif
int uh_server_init(struct uh_server *srv, struct ev_loop *loop, const char *host, int port);
#endif

View File

@ -1,170 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "utils.h"
void uh_printf(struct uh_client *cl, const char *format, ...)
{
va_list arg;
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
va_start(arg, format);
ustream_vprintf(cl->us, format, arg);
va_end(arg);
}
void uh_vprintf(struct uh_client *cl, const char *format, va_list arg)
{
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
ustream_vprintf(cl->us, format, arg);
}
void uh_chunk_send(struct uh_client *cl, const void *data, int len)
{
struct ustream *us = cl->us;
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
ustream_printf(us, "%X\r\n", len);
ustream_write(us, data, len, true);
ustream_printf(us, "\r\n", len);
}
void uh_chunk_printf(struct uh_client *cl, const char *format, ...)
{
va_list arg;
va_start(arg, format);
uh_chunk_vprintf(cl, format, arg);
va_end(arg);
}
void uh_chunk_vprintf(struct uh_client *cl, const char *format, va_list arg)
{
struct ustream *us = cl->us;
char buf[256];
va_list arg2;
int len;
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
va_copy(arg2, arg);
len = vsnprintf(buf, sizeof(buf), format, arg2);
va_end(arg2);
ustream_printf(us, "%X\r\n", len);
if (len < sizeof(buf))
ustream_write(cl->us, buf, len, true);
else
ustream_vprintf(cl->us, format, arg);
ustream_printf(us, "\r\n", len);
}
char *uh_split_header(char *str)
{
char *val;
val = strchr(str, ':');
if (!val)
return NULL;
*val = 0;
val++;
while (isspace(*val))
val++;
return val;
}
/* blen is the size of buf; slen is the length of src. The input-string need
** not be, and the output string will not be, null-terminated. Returns the
** length of the decoded string, -1 on buffer overflow, -2 on malformed string. */
int uh_urldecode(char *buf, int blen, const char *src, int slen)
{
int i;
int len = 0;
#define hex(x) \
(((x) <= '9') ? ((x) - '0') : \
(((x) <= 'F') ? ((x) - 'A' + 10) : \
((x) - 'a' + 10)))
for (i = 0; (i < slen) && (len < blen); i++)
{
if (src[i] != '%') {
buf[len++] = src[i];
continue;
}
if (i + 2 >= slen || !isxdigit(src[i + 1]) || !isxdigit(src[i + 2]))
return -2;
buf[len++] = (char)(16 * hex(src[i+1]) + hex(src[i+2]));
i += 2;
}
buf[len] = 0;
return (i == slen) ? len : -1;
}
/* blen is the size of buf; slen is the length of src. The input-string need
** not be, and the output string will not be, null-terminated. Returns the
** length of the encoded string, or -1 on error (buffer overflow) */
int uh_urlencode(char *buf, int blen, const char *src, int slen)
{
int i;
int len = 0;
static const char hex[] = "0123456789abcdef";
for (i = 0; (i < slen) && (len < blen); i++)
{
if( isalnum(src[i]) || (src[i] == '-') || (src[i] == '_') ||
(src[i] == '.') || (src[i] == '~') )
{
buf[len++] = src[i];
}
else if ((len+3) <= blen)
{
buf[len++] = '%';
buf[len++] = hex[(src[i] >> 4) & 15];
buf[len++] = hex[ src[i] & 15];
}
else
{
len = -1;
break;
}
}
return (i == slen) ? len : -1;
}
int find_idx(const char *const *list, int max, const char *str)
{
int i;
for (i = 0; i < max; i++)
if (!strcmp(list[i], str))
return i;
return -1;
}

View File

@ -1,40 +1,43 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
* MIT License
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* Copyright (c) 2019 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* 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
* Lesser General Public License for more details.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _UTILS_H
#define _UTILS_H
#ifndef _UH_UTILS_H
#define _UH_UTILS_H
#include "client.h"
#include <stddef.h>
#include <stdbool.h>
#include <inttypes.h>
#define min(x, y) (((x) < (y)) ? (x) : (y))
#define max(x, y) (((x) > (y)) ? (x) : (y))
#include "config.h"
void uh_printf(struct uh_client *cl, const char *format, ...);
void uh_vprintf(struct uh_client *cl, const char *format, va_list arg);
void uh_chunk_send(struct uh_client *cl, const void *data, int len);
void uh_chunk_printf(struct uh_client *cl, const char *format, ...);
void uh_chunk_vprintf(struct uh_client *cl, const char *format, va_list arg);
char *uh_split_header(char *str);
int uh_urldecode(char *buf, int blen, const char *src, int slen);
int uh_urlencode(char *buf, int blen, const char *src, int slen);
int find_idx(const char *const *list, int max, const char *str);
#ifndef container_of
#define container_of(ptr, type, member) \
({ \
const __typeof__(((type *) NULL)->member) *__mptr = (ptr); \
(type *) ((char *) __mptr - offsetof(type, member)); \
})
#endif
#endif