Use libev instead of libubox

Signed-off-by: Jianhui Zhao <jianhuizhao329@gmail.com>
main
Jianhui Zhao 3 years ago
parent b8248347fa
commit 471e4fe436
  1. 3
      .gitmodules
  2. 3
      CMakeLists.txt
  3. 29
      cmake/Modules/FindLibev.cmake
  4. 17
      cmake/Modules/FindLibubox.cmake
  5. 0
      cmake/Modules/FindLibuhttpd.cmake
  6. 37
      cmake/Modules/FindMbedTLS.cmake
  7. 62
      cmake/Modules/FindWolfSSL.cmake
  8. 9
      example/CMakeLists.txt
  9. 118
      example/example.c
  10. 124
      example/helloworld.c
  11. 110
      example/lua/helloworld.lua
  12. 114
      example/template.c
  13. 11
      example/template.html
  14. 132
      src/CMakeLists.txt
  15. 1
      src/buffer
  16. 652
      src/client.c
  17. 136
      src/client.h
  18. 46
      src/config.h.in
  19. 506
      src/connection.c
  20. 104
      src/connection.h
  21. 412
      src/file.c
  22. 44
      src/file.h
  23. 2498
      src/http_parser.c
  24. 439
      src/http_parser.h
  25. 138
      src/log.c
  26. 43
      src/log.h
  27. 10
      src/lua/CMakeLists.txt
  28. 582
      src/lua/uhttpd-lua.c
  29. 47
      src/lua/uhttpd-lua.h
  30. 653
      src/lua_template.c
  31. 267
      src/ssl.c
  32. 82
      src/ssl.h
  33. 110
      src/uh_ssl.c
  34. 53
      src/uh_ssl.h
  35. 202
      src/uhttpd.c
  36. 79
      src/uhttpd.h
  37. 170
      src/utils.c
  38. 63
      src/utils.h

3
.gitmodules vendored

@ -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 ()
if (UHTTPD_LUA_SUPPORT)
if (NOT LUA_FOUND)
message(FATAL_ERROR "Lua was not found on your system")
endif (NOT LUA_FOUND)
include_directories(${LUA_INCLUDE_DIR})
list(APPEND EXTRA_LIBS ${LUA_LIBRARY})
list(APPEND SOURCE_FILES lua_template.c)
add_subdirectory(lua)
else ()
set(UHTTPD_LUA_SUPPORT_CONFIG 0)
endif ()
else()
find_package(OpenSSL)
find_package(WolfSSL)
find_package(MbedTLS)
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()
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()
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

@ -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")) {