Use libev instead of libubox
Signed-off-by: Jianhui Zhao <jianhuizhao329@gmail.com>main
parent
b8248347fa
commit
471e4fe436
|
@ -0,0 +1,3 @@
|
|||
[submodule "src/buffer"]
|
||||
path = src/buffer
|
||||
url = https://github.com/zhaojh329/buffer
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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,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)
|
|
@ -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()
|
|
@ -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})
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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()
|
|
@ -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;
|
||||
}
|
|
@ -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%>
|
|
@ -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
|
||||
${CMAKE_CURRENT_BINARY_DIR}/config.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/uhttpd.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/client.h
|
||||
uhttpd.h
|
||||
connection.h
|
||||
log.h
|
||||
buffer/buffer.h
|
||||
http_parser.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/config.h
|
||||
DESTINATION
|
||||
include/uhttpd
|
||||
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}")
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 7b1ba65e2c79be169ef49f1d43524151ab293302
|
652
src/client.c
652
src/client.c
|
@ -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);
|
||||
}
|
||||
|
136
src/client.h
136
src/client.h
|
@ -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
|
|
@ -1,31 +1,39 @@
|
|||
/*
|
||||
* 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
|
||||
#define _UHTTPD_CONFIG_H
|
||||
|
||||
#define UHTTPD_VERSION_MAJOR @UHTTPD_VERSION_MAJOR@
|
||||
#define UHTTPD_VERSION_MINOR @UHTTPD_VERSION_MINOR@
|
||||
#define UHTTPD_VERSION_PATCH @UHTTPD_VERSION_PATCH@
|
||||
#define UHTTPD_VERSION_STRING "@UHTTPD_VERSION_MAJOR@.@UHTTPD_VERSION_MINOR@.@UHTTPD_VERSION_PATCH@"
|
||||
#define UHTTPD_VERSION_MAJOR @UHTTPD_VERSION_MAJOR@
|
||||
#define UHTTPD_VERSION_MINOR @UHTTPD_VERSION_MINOR@
|
||||
#define UHTTPD_VERSION_PATCH @UHTTPD_VERSION_PATCH@
|
||||
#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_SSL_SUPPORT @UHTTPD_SSL_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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
412
src/file.c
412
src/file.c
|
@ -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;
|
||||
}
|
||||
|
44
src/file.h
44
src/file.h
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
142
src/log.c
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
43
src/log.h
43
src/log.h
|
@ -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)
|
||||
|
|
|
@ -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}
|
||||
)
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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, """, 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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
110
src/uh_ssl.c
110
src/uh_ssl.c
|
@ -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);
|
||||
}
|
53
src/uh_ssl.h
53
src/uh_ssl.h
|
@ -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
|
216
src/uhttpd.c
216
src/uhttpd.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
79
src/uhttpd.h
79
src/uhttpd.h
|
@ -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
|
||||
|
||||
|
|
170
src/utils.c
170
src/utils.c
|
@ -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;
|
||||
}
|
61
src/utils.h
61
src/utils.h
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue