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