View
@@ -5,69 +5,60 @@ TechEmpower Web Framework Benchmarks
<!--#declaration
#include "world.h"
static World* pworld;
static char buffer[128];
static UREDISClient_Base* rc;
#ifndef AS_cpoll_cppsp_DO
static UValue* pvalue;
static UValue* pvalue;
#endif
static void usp_fork_rdb()
{
U_TRACE(5, "::usp_fork_rdb()")
U_TRACE(5, "::usp_fork_rdb()")
rc = U_NEW(UREDISClient<UTCPSocket>);
U_NEW(UREDISClient<UTCPSocket>, rc, UREDISClient<UTCPSocket>);
if (rc->connect() == false)
{
U_WARNING("usp_fork_rdb(): %V", rc->UClient_Base::getResponse().rep);
if (rc->connect() == false)
{
U_WARNING("usp_fork_rdb(): %V", rc->UClient_Base::getResponse().rep);
return;
}
return;
}
pworld = U_NEW(World);
u__memcpy(buffer, "world:", U_CONSTANT_SIZE("world:"), __PRETTY_FUNCTION__);
#ifndef AS_cpoll_cppsp_DO
pvalue = U_NEW(UValue(OBJECT_VALUE));
U_NEW(UValue, pvalue, UValue(OBJECT_VALUE));
#endif
u__memcpy(u_buffer, "world:", U_CONSTANT_SIZE("world:"), __PRETTY_FUNCTION__);
}
#ifdef DEBUG
static void usp_end_rdb()
{
U_TRACE(5, "::usp_end_rdb()")
delete rc;
U_TRACE(5, "::usp_end_rdb()")
if (pworld)
{
delete pworld;
delete rc;
# ifndef AS_cpoll_cppsp_DO
delete pvalue;
# endif
}
#ifndef AS_cpoll_cppsp_DO
if (pvalue) delete pvalue;
#endif
}
#endif
-->
<!--#header
Content-Type: application/json
-->
<!--#code
UStringRep* rep;
(void) rc->get(u_buffer, 6+u_num2str32(u_buffer+6, pworld->id = u_get_num_random(10000)));
rep = rc->vitem[0].rep;
uint32_t id;
pworld->randomNumber = u_strtoul(rep->data(), rep->end());
(void) rc->get(buffer, 6+u_num2str32(buffer+6, id = u_get_num_random(10000-1)));
#ifdef AS_cpoll_cppsp_DO
USP_PRINTF_ADD("{\"id\":%u,\"randomNumber\":%v}", pworld->id, rep);
USP_PRINTF_ADD("{\"id\":%u,\"randomNumber\":%v}", id, rc->vitem[0].rep);
#else
USP_JSON_stringify(*pvalue, World, *pworld);
UStringRep* rep = rc->vitem[0].rep;
World world(id, u_strtoul(rep->data(), rep->end()));
USP_JSON_stringify(*pvalue, World, world);
pvalue->clear();
#endif
-->
View
@@ -14,7 +14,7 @@ static void usp_fork_rfortune()
{
U_TRACE(5, "::usp_fork_rfortune()")
rc = U_NEW(UREDISClient<UTCPSocket>);
U_NEW(UREDISClient<UTCPSocket>, rc, UREDISClient<UTCPSocket>);
if (rc->connect() == false)
{
@@ -23,9 +23,9 @@ static void usp_fork_rfortune()
return;
}
pencoded = U_NEW(UString(100U));
pvfortune = U_NEW(UVector<Fortune*>);
pfortune2add = U_NEW(Fortune(0, U_STRING_FROM_CONSTANT("Additional fortune added at request time.")));
U_NEW(UString, pencoded, UString(100U));
U_NEW(UVector<Fortune*>, pvfortune, UVector<Fortune*>);
U_NEW(Fortune, pfortune2add, Fortune(0, U_STRING_FROM_CONSTANT("Additional fortune added at request time.")));
}
#ifdef DEBUG
@@ -45,13 +45,21 @@ static void usp_end_rfortune()
#endif
-->
<!doctype html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr><!--#code
Fortune* item;
uint32_t i, n;
U_NEW(Fortune, item, Fortune(*pfortune2add));
pvfortune->push_back(item);
(void) rc->lrange(U_CONSTANT_TO_PARAM("fortunes 0 -1"));
pvfortune->push_back(U_NEW(Fortune(*pfortune2add)));
for (i = 0, n = rc->vitem.size(); i < n; ++i)
{
U_NEW(Fortune, item, Fortune(i+1, rc->vitem[i]));
for (i = 0, n = rc->vitem.size(); i < n; ++i) pvfortune->push_back(U_NEW(Fortune(i+1, rc->vitem[i])));
pvfortune->push_back(item);
}
pvfortune->sort(Fortune::cmp_obj);
View
@@ -5,48 +5,49 @@ TechEmpower Web Framework Benchmarks
<!--#declaration
#include "world.h"
static char buffer[128];
static UREDISClient_Base* rc;
static UVector<World*>* pvworld_query;
static UVector<World*>* pvworld_query;
#ifndef AS_cpoll_cppsp_DO
static UValue* pvalue;
static UValue* pvalue;
#endif
static void usp_fork_rquery()
{
U_TRACE(5, "::usp_fork_rquery()")
U_TRACE(5, "::usp_fork_rquery()")
rc = U_NEW(UREDISClient<UTCPSocket>);
U_NEW(UREDISClient<UTCPSocket>, rc, UREDISClient<UTCPSocket>);
if (rc->connect() == false)
{
U_WARNING("usp_fork_rquery(): %V", rc->UClient_Base::getResponse().rep);
if (rc->connect() == false)
{
U_WARNING("usp_fork_rquery(): %V", rc->UClient_Base::getResponse().rep);
return;
}
return;
}
pvworld_query = U_NEW(UVector<World*>(500));
U_NEW(UVector<World*>, pvworld_query, UVector<World*>(500));
#ifndef AS_cpoll_cppsp_DO
pvalue = U_NEW(UValue(ARRAY_VALUE));
U_NEW(UValue, pvalue, UValue(ARRAY_VALUE));
#endif
}
#ifdef DEBUG
static void usp_end_rquery()
{
U_TRACE(5, "::usp_end_rquery()")
U_TRACE(5, "::usp_end_rquery()")
delete rc;
delete rc;
if (pvworld_query)
{
delete pvworld_query;
if (pvworld_query)
{
delete pvworld_query;
# ifndef AS_cpoll_cppsp_DO
delete pvalue;
# endif
}
# ifndef AS_cpoll_cppsp_DO
delete pvalue;
# endif
}
}
#endif
-->
@@ -56,44 +57,46 @@ Content-Type: application/json
<!--#code
World* pworld;
UStringRep* rep;
char* pbuffer = u_buffer;
char* pbuffer = buffer;
int i, num_queries = UHTTP::getFormFirstNumericValue(1, 500);
#ifdef AS_cpoll_cppsp_DO
USP_PUTS_CHAR('[');
#endif
for (i = 0; i < num_queries; ++i)
{
pvworld_query->push_back(pworld = U_NEW(World));
{
U_NEW(World, pworld, World);
pvworld_query->push_back(pworld);
u_put_unalignedp64(pbuffer, U_MULTICHAR_CONSTANT64(' ','w','o','r','l','d',':','\0'));
u_put_unalignedp64(pbuffer, U_MULTICHAR_CONSTANT64(' ','w','o','r','l','d',':','\0'));
pbuffer += 7+u_num2str32(pbuffer+7, pworld->id = u_get_num_random(10000));
}
pbuffer += 7+u_num2str32(pbuffer+7, pworld->id = u_get_num_random(10000-1));
}
(void) rc->mget(u_buffer, pbuffer-u_buffer);
(void) rc->mget(buffer, pbuffer-buffer);
i = 0;
while (true)
{
pworld = pvworld_query->at(i);
pworld = pvworld_query->at(i);
rep = rc->vitem[i].rep;
rep = rc->vitem[i].rep;
pworld->randomNumber = u_strtoul(rep->data(), rep->end());
pworld->randomNumber = u_strtoul(rep->data(), rep->end());
#ifdef AS_cpoll_cppsp_DO
USP_PRINTF("{\"id\":%u,\"randomNumber\":%v}", pworld->id, rep);
USP_PRINTF("{\"id\":%u,\"randomNumber\":%v}", pworld->id, rep);
#endif
if (++i == num_queries) break;
#ifdef AS_cpoll_cppsp_DO
USP_PUTS_CHAR(',');
USP_PUTS_CHAR(',');
#endif
}
}
#ifdef AS_cpoll_cppsp_DO
USP_PUTS_CHAR(']');
View
@@ -5,48 +5,49 @@ TechEmpower Web Framework Benchmarks
<!--#declaration
#include "world.h"
static char buffer[128];
static UREDISClient_Base* rc;
static UVector<World*>* pvworld;
#ifndef AS_cpoll_cppsp_DO
static UValue* pvalue;
static UValue* pvalue;
#endif
static void usp_fork_rupdate()
{
U_TRACE(5, "::usp_fork_rupdate()")
U_TRACE(5, "::usp_fork_rupdate()")
rc = U_NEW(UREDISClient<UTCPSocket>);
U_NEW(UREDISClient<UTCPSocket>, rc, UREDISClient<UTCPSocket>);
if (rc->connect() == false)
{
U_WARNING("usp_fork_rupdate(): %V", rc->UClient_Base::getResponse().rep);
if (rc->connect() == false)
{
U_WARNING("usp_fork_rupdate(): %V", rc->UClient_Base::getResponse().rep);
return;
}
return;
}
pvworld = U_NEW(UVector<World*>(500));
U_NEW(UVector<World*>, pvworld, UVector<World*>(500));
#ifndef AS_cpoll_cppsp_DO
pvalue = U_NEW(UValue(ARRAY_VALUE));
U_NEW(UValue, pvalue, UValue(ARRAY_VALUE));
#endif
}
#ifdef DEBUG
static void usp_end_rupdate()
{
U_TRACE(5, "::usp_end_rupdate()")
U_TRACE(5, "::usp_end_rupdate()")
delete rc;
delete rc;
if (pvworld)
{
delete pvworld;
if (pvworld)
{
delete pvworld;
# ifndef AS_cpoll_cppsp_DO
delete pvalue;
# endif
}
# ifndef AS_cpoll_cppsp_DO
delete pvalue;
# endif
}
}
#endif
-->
@@ -55,49 +56,51 @@ Content-Type: application/json
-->
<!--#code
World* pworld;
char* pbuffer = u_buffer;
char* pbuffer = buffer;
int i, num_queries = UHTTP::getFormFirstNumericValue(1, 500);
#ifdef AS_cpoll_cppsp_DO
USP_PUTS_CHAR('[');
#endif
for (i = 0; i < num_queries; ++i)
{
pvworld->push_back(pworld = U_NEW(World));
{
U_NEW(World, pworld, World);
pvworld->push_back(pworld);
u_put_unalignedp64(pbuffer, U_MULTICHAR_CONSTANT64(' ','w','o','r','l','d',':','\0'));
u_put_unalignedp64(pbuffer, U_MULTICHAR_CONSTANT64(' ','w','o','r','l','d',':','\0'));
pbuffer += 7+u_num2str32(pbuffer+7, pworld->id = u_get_num_random(10000));
}
pbuffer += 7+u_num2str32(pbuffer+7, pworld->id = u_get_num_random(10000-1));
}
(void) rc->mget(u_buffer, pbuffer-u_buffer);
(void) rc->mget(buffer, pbuffer-buffer);
i = 0;
pbuffer = u_buffer;
pbuffer = buffer;
while (true)
{
pworld = pvworld->at(i);
pworld = pvworld->at(i);
u_put_unalignedp64(pbuffer, U_MULTICHAR_CONSTANT64(' ','w','o','r','l','d',':','\0'));
u_put_unalignedp64(pbuffer, U_MULTICHAR_CONSTANT64(' ','w','o','r','l','d',':','\0'));
pbuffer += 7+u_num2str32(pbuffer+7, pworld->id);
pbuffer += 7+u_num2str32(pbuffer+7, pworld->id);
*pbuffer = ' ';
pbuffer += 1+u_num2str32(pbuffer+1, pworld->randomNumber = u_get_num_random(10000));
pbuffer += 1+u_num2str32(pbuffer+1, pworld->randomNumber = u_get_num_random(10000-1));
#ifdef AS_cpoll_cppsp_DO
USP_PRINTF("{\"id\":%u,\"randomNumber\":%u}", pworld->id, pworld->randomNumber);
USP_PRINTF("{\"id\":%u,\"randomNumber\":%u}", pworld->id, pworld->randomNumber);
#endif
if (++i == num_queries) break;
#ifdef AS_cpoll_cppsp_DO
USP_PUTS_CHAR(',');
USP_PUTS_CHAR(',');
#endif
}
}
(void) rc->mset(u_buffer, pbuffer-u_buffer);
(void) rc->mset(buffer, pbuffer-buffer);
#ifdef AS_cpoll_cppsp_DO
USP_PUTS_CHAR(']');
View
@@ -19,30 +19,28 @@ static void usp_fork_update()
{
U_TRACE(5, "::usp_fork_update()")
psql_update = U_NEW(UOrmSession(U_CONSTANT_TO_PARAM("hello_world")));
U_NEW(UOrmSession, psql_update, UOrmSession(U_CONSTANT_TO_PARAM("hello_world")));
if (psql_update->isReady())
{
pstmt1 = U_NEW(UOrmStatement(*psql_update, U_CONSTANT_TO_PARAM("SELECT randomNumber FROM World WHERE id = ?")));
pstmt2 = U_NEW(UOrmStatement(*psql_update, U_CONSTANT_TO_PARAM("UPDATE World SET randomNumber = ? WHERE id = ?")));
U_NEW(UOrmStatement, pstmt1, UOrmStatement(*psql_update, U_CONSTANT_TO_PARAM("SELECT randomNumber FROM World WHERE id = ?")));
U_NEW(UOrmStatement, pstmt2, UOrmStatement(*psql_update, U_CONSTANT_TO_PARAM("UPDATE World SET randomNumber = ? WHERE id = ?")));
if (pstmt1 == 0 ||
pstmt2 == 0)
{
U_WARNING("usp_fork_update(): we cound't connect to db");
}
if (UOrmDriver::isPGSQL()) *psql_update << "SET synchronous_commit TO OFF";
pworld_update = U_NEW(World);
U_NEW(World, pworld_update, World);
pstmt1->use( pworld_update->id);
pstmt1->into(pworld_update->randomNumber);
pstmt2->use( pworld_update->randomNumber, pworld_update->id);
# ifndef AS_cpoll_cppsp_DO
pvalue = U_NEW(UValue(ARRAY_VALUE));
pvworld_update = U_NEW(UVector<World*>(500));
U_NEW(UValue, pvalue, UValue(ARRAY_VALUE));
U_NEW(UVector<World*>, pvworld_update, UVector<World*>(500));
# endif
}
}
@@ -80,18 +78,22 @@ USP_PUTS_CHAR('[');
while (true)
{
pworld_update->id = u_get_num_random(10000);
pworld_update->id = u_get_num_random(10000-1);
pstmt1->execute();
pworld_update->randomNumber = u_get_num_random(10000);
pworld_update->randomNumber = u_get_num_random(10000-1);
pstmt2->execute();
#ifdef AS_cpoll_cppsp_DO
USP_PRINTF("{\"id\":%u,\"randomNumber\":%u}", pworld_update->id, pworld_update->randomNumber);
#else
pvworld_update->push_back(U_NEW(World(*pworld_update)));
World* pworld;
U_NEW(World, pworld, World(*pworld_update));
pvworld_update->push_back(pworld);
#endif
if (++i == num_queries) break;
View
@@ -27,11 +27,9 @@ class World {
# endif
}
World(const World& w) : id(w.id), randomNumber(w.randomNumber)
World(uint32_t _id, uint32_t _randomNumber) : id(_id), randomNumber(_randomNumber)
{
U_TRACE_REGISTER_OBJECT(5, World, "%p", &w)
U_MEMORY_TEST_COPY(w)
U_TRACE_REGISTER_OBJECT(5, World, "%u,%u", _id, _randomNumber)
}
~World()
View
@@ -0,0 +1,2 @@
/benchmark.wt
/benchmark_postgres.wt
View
@@ -1,3 +1,5 @@
*.o
*~
/Makefile
/main.d
/ws.duda
View
@@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 2.8.0)
project(h2o_app)
find_library(H2O_LIB h2o-evloop)
find_library(MUSTACHE_C_LIB mustache_c)
find_library(YAJL_LIB yajl)
find_path(H2O_INCLUDE h2o.h)
find_path(MUSTACHE_C_INCLUDE mustache.h)
find_path(YAJL_INCLUDE yajl/yajl_gen.h)
set(COMMON_OPTIONS -flto -pthread)
add_compile_options(-std=gnu11 -pedantic -Wall -Wextra ${COMMON_OPTIONS})
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fstack-protector-all -D_FORTIFY_SOURCE=2")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Ofast")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -Ofast")
add_definitions(-DH2O_USE_LIBUV=0)
include_directories(src ${H2O_INCLUDE} ${MUSTACHE_C_INCLUDE} ${YAJL_INCLUDE})
file(GLOB SOURCES "src/*.c")
add_executable(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME} ${COMMON_OPTIONS})
target_link_libraries(${PROJECT_NAME} ${H2O_LIB} ssl crypto pq ${MUSTACHE_C_LIB} ${YAJL_LIB})
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin)
file(GLOB TEMPLATES "template/*")
install(FILES ${TEMPLATES} DESTINATION share/${PROJECT_NAME}/template)
View
@@ -0,0 +1,11 @@
# h2o
This is a framework implementation using the [H2O](https://h2o.examp1e.net/) HTTP server.
## Requirements
CMake, H2O, libpq, mustache-c, OpenSSL, YAJL
## Contact
Anton Kirilov <antonvkirilov@gmail.com>
View
@@ -1,8 +1,8 @@
{
"framework": "moonshine",
"framework": "h2o",
"tests": [{
"default": {
"setup_file": "setup-postgres",
"setup_file": "setup",
"json_url": "/json",
"db_url": "/db",
"query_url": "/queries?queries=",
@@ -11,19 +11,18 @@
"plaintext_url": "/plaintext",
"port": 8080,
"approach": "Realistic",
"classification": "Fullstack",
"classification": "Platform",
"database": "Postgres",
"framework": "moonshine",
"language": "Crystal",
"flavor": "Crystal12",
"orm": "micro",
"framework": "None",
"language": "C",
"flavor": "C11",
"orm": "Raw",
"platform": "None",
"webserver": "None",
"os": "Linux",
"database_os": "Linux",
"display_name": "Moonshine",
"notes": "",
"versus": "crystal"
"display_name": "H2O",
"notes": ""
}
}]
}
View
@@ -0,0 +1,57 @@
#!/bin/bash
fw_depends h2o mustache-c yajl
H2O_APP_HOME="${IROOT}/h2o_app"
BUILD_DIR="${H2O_APP_HOME}_build"
H2O_APP_PROFILE_PORT="54321"
H2O_APP_PROFILE_URL="http://127.0.0.1:$H2O_APP_PROFILE_PORT"
build_h2o_app()
{
cmake -DCMAKE_INSTALL_PREFIX="$H2O_APP_HOME" -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_LIBRARY_PATH="${H2O_HOME}/lib;${MUSTACHE_C_HOME}/lib;${YAJL_HOME}/lib" \
-DCMAKE_INCLUDE_PATH="${H2O_HOME}/include;${MUSTACHE_C_HOME}/include;${YAJL_HOME}/include" \
-DCMAKE_C_FLAGS="-march=native $1" "$TROOT"
make -j "$(nproc)"
}
run_curl()
{
for ((i = 0; i < 10; i++)); do
curl "${H2O_APP_PROFILE_URL}/$1" > /dev/null 2>&1
done
}
run_h2o_app()
{
"$1/h2o_app" -a2 -f "$2/template/fortunes.mustache" -m2 "$3" "$4" \
-d "host=$DBHOST dbname=hello_world user=benchmarkdbuser password=benchmarkdbpass" &
}
generate_profile_data()
{
run_h2o_app . "${TROOT}" -p$H2O_APP_PROFILE_PORT -t1
local -r H2O_APP_PROFILE_PID=$!
while ! curl ${H2O_APP_PROFILE_URL} > /dev/null 2>&1; do sleep 1; done
run_curl json
run_curl db
run_curl queries?queries=20
run_curl fortunes
run_curl updates?queries=20
run_curl plaintext
kill -s SIGTERM $H2O_APP_PROFILE_PID
wait $H2O_APP_PROFILE_PID
}
install -d "$BUILD_DIR"
pushd "$BUILD_DIR"
build_h2o_app "-fprofile-generate"
generate_profile_data
make clean
rm -f CMakeCache.txt
build_h2o_app "-fprofile-use"
make -j "$(nproc)" install
popd
rm -rf "$BUILD_DIR"
run_h2o_app "${H2O_APP_HOME}/bin" "${H2O_APP_HOME}/share/h2o_app"
View
@@ -0,0 +1,23 @@
./src/bitset.h
./src/database.c
./src/database.h
./src/error.c
./src/error.h
./src/event_loop.c
./src/event_loop.h
./src/fortune.c
./src/fortune.h
./src/list.h
./src/main.c
./src/request_handler.c
./src/request_handler.h
./src/template.c
./src/template.h
./src/thread.c
./src/thread.h
./src/tls.c
./src/tls.h
./src/utility.c
./src/utility.h
./src/world.c
./src/world.h
View
@@ -0,0 +1,60 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 BITSET_H_
#define BITSET_H_
#include <assert.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include "utility.h"
typedef uint_fast32_t bitset_base_t;
#define BITSET_ISSET(i, b) bitset_isset((i), (b), sizeof(b) * CHAR_BIT)
#define BITSET_SET(i, b) bitset_set((i), (b), sizeof(b) * CHAR_BIT)
// Use a designated initializer to set all array elements to zero.
#define DEFINE_BITSET(b, s) \
assert(s); \
bitset_base_t (b)[((s) - 1) / (sizeof(bitset_base_t) * CHAR_BIT) + 1] = {[0] = 0}
static inline void bitset_set(size_t i, bitset_base_t *b, size_t num)
{
assert(i < num);
IGNORE_FUNCTION_PARAMETER(num);
const bitset_base_t mask = ((bitset_base_t) 1) << (i % (sizeof(*b) * CHAR_BIT));
b[i / (sizeof(*b) * CHAR_BIT)] |= mask;
}
static inline bool bitset_isset(size_t i, bitset_base_t *b, size_t num)
{
assert(i < num);
IGNORE_FUNCTION_PARAMETER(num);
return (b[i / (sizeof(*b) * CHAR_BIT)] >> (i % (sizeof(*b) * CHAR_BIT))) & (bitset_base_t) 1;
}
#endif // BITSET_H_
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -0,0 +1,88 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 DATABASE_H_
#define DATABASE_H_
#include <h2o.h>
#include <stdint.h>
#include <postgresql/libpq-fe.h>
#include "list.h"
#define COPY_HEADER "PGCOPY\n\377\r\n\0\0\0\0\0\0\0\0\0"
#define DB_REQ_ERROR "too many concurrent database requests\n"
#define DB_TIMEOUT_ERROR "database timeout\n"
#define FORTUNE_TABLE_NAME "Fortune"
#define ID_FIELD_NAME "id"
#define IS_PREPARED 1
#define IS_SINGLE_ROW 2
#define MAX_ID 10000
#define MESSAGE_FIELD_NAME "message"
#define WORLD_TABLE_NAME "World"
#define UPDATE_QUERY \
"CREATE TEMP TABLE input(LIKE World INCLUDING ALL) ON COMMIT DROP;" \
"COPY input FROM STDIN WITH (FORMAT binary);" \
"UPDATE World SET randomNumber = input.randomNumber FROM input " \
"WHERE World." ID_FIELD_NAME " = input." ID_FIELD_NAME ";"
typedef enum {
SUCCESS,
DONE,
WANT_WRITE
} result_return_t;
typedef struct thread_context_t thread_context_t;
typedef struct db_query_param_t db_query_param_t;
typedef result_return_t (*on_result_t)(db_query_param_t *, PGresult *);
struct db_query_param_t {
list_t l;
void (*on_error)(db_query_param_t *, const char *);
on_result_t on_result;
void (*on_timeout)(db_query_param_t *);
int (*on_write_ready)(db_query_param_t *, PGconn *);
const char *command;
const char * const *paramValues;
const int *paramLengths;
const int *paramFormats;
size_t nParams;
uint_fast32_t flags;
int resultFormat;
};
typedef struct {
list_t *db_conn;
queue_t queries;
size_t db_conn_num;
size_t free_db_conn_num;
size_t query_num;
h2o_timeout_t h2o_timeout;
} db_state_t;
void connect_to_database(thread_context_t *ctx);
int execute_query(thread_context_t *ctx, db_query_param_t *param);
void free_database_state(h2o_loop_t *loop, db_state_t *db_state);
void initialize_database_state(h2o_loop_t *loop, db_state_t *db_state);
#endif // DATABASE_H_
View
@@ -0,0 +1,66 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 <libgen.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include "error.h"
void print_error(const char *file,
unsigned line,
const char *function,
const char *error_string, ...)
{
char * const file_name = strdup(file);
if (file_name)
file = basename(file_name);
flockfile(stderr);
fprintf(stderr, "[%d] %s: %u: %s(): ", (int) syscall(SYS_gettid), file, line, function);
va_list arg;
va_start(arg, error_string);
vfprintf(stderr, error_string, arg);
va_end(arg);
putc_unlocked('\n', stderr);
funlockfile(stderr);
if (file_name)
free(file_name);
}
void print_library_error(const char *file,
unsigned line,
const char *function,
int error_code)
{
char error_string[128];
if (strerror_r(error_code, error_string, sizeof(error_string)))
*error_string = '\0';
print_error(file, line, function, "%s (%d)", error_string, error_code);
}
View
@@ -0,0 +1,85 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 ERROR_H_
#define ERROR_H_
#include <errno.h>
#include <stdarg.h>
#include <yajl/yajl_gen.h>
#define EPOLL_ERR_MSG "epoll_ctl() failure\n"
#define MEM_ALLOC_ERR_MSG "memory allocation failure\n"
#define CHECK_ERRNO(function, ...) \
do { \
const int error_code = (function)(__VA_ARGS__); \
\
if (error_code) { \
print_library_error(__FILE__, __LINE__, #function, errno); \
abort(); \
} \
} while(0)
#define CHECK_ERRNO_RETURN(out, function, ...) \
do { \
const int return_value = (function)(__VA_ARGS__); \
\
if (return_value == -1) { \
print_library_error(__FILE__, __LINE__, #function, errno); \
abort(); \
} \
\
(out) = return_value; \
} while(0)
#define CHECK_ERROR(function, ...) \
do { \
const int error_code = (function)(__VA_ARGS__); \
\
if (error_code) { \
print_library_error(__FILE__, __LINE__, #function, error_code); \
abort(); \
} \
} while(0)
#define CHECK_YAJL_STATUS(function, ...) \
do { \
const yajl_gen_status status_code = (function)(__VA_ARGS__); \
\
if (status_code != yajl_gen_status_ok) { \
print_error(__FILE__, __LINE__, #function, "Error (%d)", (int) status_code); \
goto error_yajl; \
} \
} while(0)
#define ERROR(...) print_error(__FILE__, __LINE__, __func__, __VA_ARGS__)
#define LIBRARY_ERROR(function) print_library_error(__FILE__, __LINE__, (function), errno)
void print_error(const char *file,
unsigned line,
const char *function,
const char *error_string, ...);
void print_library_error(const char *file,
unsigned line,
const char *function,
int error_code);
#endif // ERROR_H_
View
@@ -0,0 +1,205 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 <errno.h>
#include <fcntl.h>
#include <h2o.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/syscall.h>
#include "error.h"
#include "event_loop.h"
#include "thread.h"
#include "utility.h"
static void accept_connection(h2o_socket_t *listener, const char *err);
static void do_epoll_wait(h2o_socket_t *epoll_sock, const char *err);
static void on_close_connection(void *data);
static void process_messages(h2o_multithread_receiver_t *receiver, h2o_linklist_t *messages);
static void shutdown_server(h2o_socket_t *listener, const char *err);
static void accept_connection(h2o_socket_t *listener, const char *err)
{
if (!err) {
thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
event_loop,
listener->data);
if (!ctx->global_data->shutdown) {
size_t accepted = 0;
do {
h2o_socket_t * const sock = h2o_evloop_socket_accept(listener);
if (!sock)
break;
ctx->event_loop.conn_num++;
sock->on_close.cb = on_close_connection;
sock->on_close.data = &ctx->event_loop.conn_num;
h2o_accept(&ctx->event_loop.h2o_accept_ctx, sock);
} while (++accepted < ctx->global_data->config->max_accept);
}
}
}
static void do_epoll_wait(h2o_socket_t *epoll_sock, const char *err)
{
if (!err) {
thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
event_loop,
epoll_sock->data);
const size_t num_event = ctx->db_state.db_conn_num - ctx->db_state.free_db_conn_num;
int ready;
struct epoll_event event[num_event];
do
ready = epoll_wait(ctx->event_loop.epoll_fd, event, num_event, 0);
while (ready < 0 && errno == EINTR);
if (ready > 0)
for (size_t i = 0; i < (size_t) ready; i++) {
void (** const on_write_ready)(void *) = event[i].data.ptr;
(*on_write_ready)(on_write_ready);
}
}
}
static void on_close_connection(void *data)
{
size_t * const conn_num = data;
(*conn_num)--;
}
static void process_messages(h2o_multithread_receiver_t *receiver, h2o_linklist_t *messages)
{
IGNORE_FUNCTION_PARAMETER(messages);
thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
event_loop.h2o_receiver,
receiver);
h2o_socket_read_stop(ctx->event_loop.h2o_socket);
}
static void shutdown_server(h2o_socket_t *listener, const char *err)
{
if (!err) {
thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
event_loop,
listener->data);
ctx->global_data->shutdown = true;
h2o_socket_read_stop(ctx->event_loop.h2o_socket);
for (size_t i = 1; i < ctx->global_data->config->thread_num; i++)
h2o_multithread_send_message(&ctx[i].event_loop.h2o_receiver, NULL);
}
}
void event_loop(thread_context_t *ctx)
{
ctx->tid = syscall(SYS_gettid);
ctx->random_seed = ctx->tid;
while (!ctx->global_data->shutdown || ctx->event_loop.conn_num)
h2o_evloop_run(ctx->event_loop.h2o_ctx.loop);
}
void free_event_loop(event_loop_t *event_loop)
{
h2o_multithread_unregister_receiver(event_loop->h2o_ctx.queue, &event_loop->h2o_receiver);
h2o_socket_close(event_loop->h2o_socket);
h2o_socket_close(event_loop->epoll_socket);
h2o_context_dispose(&event_loop->h2o_ctx);
}
void initialize_event_loop(bool is_main_thread,
global_data_t *global_data,
event_loop_t *loop)
{
memset(loop, 0, sizeof(*loop));
h2o_context_init(&loop->h2o_ctx, h2o_evloop_create(), &global_data->h2o_config);
loop->h2o_accept_ctx.ctx = &loop->h2o_ctx;
loop->h2o_accept_ctx.hosts = global_data->h2o_config.hosts;
loop->h2o_accept_ctx.ssl_ctx = global_data->ssl_ctx;
int listener_sd;
if (is_main_thread)
listener_sd = global_data->listener_sd;
else {
int flags;
CHECK_ERRNO_RETURN(listener_sd, dup, global_data->listener_sd);
CHECK_ERRNO_RETURN(flags, fcntl, listener_sd, F_GETFD);
CHECK_ERRNO(fcntl, listener_sd, F_SETFD, flags | FD_CLOEXEC);
}
// Let all the threads race to call accept() on the socket; since the latter is
// non-blocking, that will effectively act as load balancing.
loop->h2o_socket = h2o_evloop_socket_create(loop->h2o_ctx.loop,
listener_sd,
H2O_SOCKET_FLAG_DONT_READ);
loop->h2o_socket->data = loop;
h2o_socket_read_start(loop->h2o_socket, accept_connection);
h2o_multithread_register_receiver(loop->h2o_ctx.queue,
&loop->h2o_receiver,
process_messages);
// libh2o's event loop does not support write polling unless it
// controls sending the data as well, so do read polling on the
// epoll file descriptor as a work-around.
CHECK_ERRNO_RETURN(loop->epoll_fd, epoll_create1, EPOLL_CLOEXEC);
loop->epoll_socket = h2o_evloop_socket_create(loop->h2o_ctx.loop,
loop->epoll_fd,
H2O_SOCKET_FLAG_DONT_READ);
loop->epoll_socket->data = loop;
h2o_socket_read_start(loop->epoll_socket, do_epoll_wait);
if (is_main_thread) {
global_data->signals = h2o_evloop_socket_create(loop->h2o_ctx.loop,
global_data->signal_fd,
H2O_SOCKET_FLAG_DONT_READ);
global_data->signals->data = loop;
h2o_socket_read_start(global_data->signals, shutdown_server);
}
}
int start_write_polling(int fd,
void (**on_write_ready)(void *),
bool rearm,
event_loop_t *event_loop)
{
struct epoll_event event;
memset(&event, 0, sizeof(event));
event.data.ptr = on_write_ready;
event.events = EPOLLET | EPOLLONESHOT | EPOLLOUT | EPOLLRDHUP;
return epoll_ctl(event_loop->epoll_fd, rearm ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, fd, &event);
}
void stop_write_polling(int fd, event_loop_t *event_loop)
{
epoll_ctl(event_loop->epoll_fd, EPOLL_CTL_DEL, fd, NULL);
}
View
@@ -0,0 +1,52 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 EVENT_LOOP_H_
#define EVENT_LOOP_H_
#include <h2o.h>
#include <stdbool.h>
#include "utility.h"
typedef struct thread_context_t thread_context_t;
typedef struct {
h2o_socket_t *epoll_socket;
h2o_socket_t *h2o_socket;
size_t conn_num;
int epoll_fd;
h2o_accept_ctx_t h2o_accept_ctx;
h2o_context_t h2o_ctx;
h2o_multithread_receiver_t h2o_receiver;
} event_loop_t;
void event_loop(thread_context_t *ctx);
void free_event_loop(event_loop_t *event_loop);
void initialize_event_loop(bool is_main_thread,
global_data_t *global_data,
event_loop_t *loop);
int start_write_polling(int fd,
void (**on_write_ready)(void *),
bool rearm,
event_loop_t *event_loop);
void stop_write_polling(int fd, event_loop_t *event_loop);
#endif // EVENT_LOOP_H_
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -0,0 +1,38 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 FORTUNE_H_
#define FORTUNE_H_
#include <h2o.h>
#include <postgresql/libpq-fe.h>
#include "list.h"
typedef struct {
list_t l;
PGresult *data;
h2o_iovec_t id;
h2o_iovec_t message;
} fortune_t;
int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req);
#endif // FORTUNE_H_
View
@@ -0,0 +1,35 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 LIST_H_
#define LIST_H_
typedef struct list_t list_t;
struct list_t {
list_t *next;
};
typedef struct {
list_t *head;
list_t **tail;
} queue_t;
#endif // LIST_H_
View
@@ -0,0 +1,366 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 <assert.h>
#include <errno.h>
#include <h2o.h>
#include <mustache.h>
#include <netdb.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <h2o/serverutil.h>
#include <netinet/tcp.h>
#include <sys/resource.h>
#include <sys/signalfd.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include "error.h"
#include "event_loop.h"
#include "request_handler.h"
#include "template.h"
#include "thread.h"
#include "tls.h"
#include "utility.h"
#define DEFAULT_TCP_FASTOPEN_QUEUE_LEN 4096
#define USAGE_MESSAGE \
"Usage:\n%s [-a <max connections accepted simultaneously>] [-b <bind address>] " \
"[-c <certificate file>] [-d <database connection string>] [-f fortunes template file path] " \
"[-k <private key file>] [-l <log path>] " \
"[-m <max database connections per thread>] [-p <port>] " \
"[-q <max enqueued database queries per thread>] [-r <root directory>] " \
"[-t <thread number>]\n"
static void free_global_data(global_data_t *global_data);
static int get_listener_socket(const config_t *config);
static size_t get_maximum_cache_line_size(void);
static int initialize_global_data(const config_t *config, global_data_t *global_data);
static int parse_options(int argc, char *argv[], config_t *config);
static void set_default_options(config_t *config);
static void setup_process(void);
static void free_global_data(global_data_t *global_data)
{
if (global_data->ctx)
free_thread_contexts(global_data);
if (global_data->file_logger)
global_data->file_logger->dispose(global_data->file_logger);
if (global_data->fortunes_template) {
mustache_api_t api = {.freedata = NULL};
mustache_free(&api, global_data->fortunes_template);
}
h2o_config_dispose(&global_data->h2o_config);
if (global_data->ssl_ctx)
cleanup_openssl(global_data);
}
static int get_listener_socket(const config_t *config)
{
int ret = -1;
char port[16];
if ((size_t) snprintf(port, sizeof(port), "%u", (unsigned) config->port) >= sizeof(port)) {
print_error(__FILE__, __LINE__, "snprintf", "Truncated output.");
return ret;
}
struct addrinfo *res = NULL;
struct addrinfo hints = {.ai_socktype = SOCK_STREAM, .ai_flags = AI_PASSIVE};
const int error_code = getaddrinfo(config->bind_address, port, &hints, &res);
if (error_code) {
print_error(__FILE__, __LINE__, "getaddrinfo", gai_strerror(error_code));
return ret;
}
struct addrinfo *iter = res;
for (; iter; iter = iter->ai_next) {
const int s = socket(iter->ai_family,
iter->ai_socktype | SOCK_NONBLOCK | SOCK_CLOEXEC,
iter->ai_protocol);
if (s == -1) {
LIBRARY_ERROR("socket");
continue;
}
#define LOCAL_CHECK_ERRNO(function, ...) \
do { \
const int error_code = (function)(__VA_ARGS__); \
\
if (error_code) { \
print_library_error(__FILE__, __LINE__, #function, errno); \
goto error; \
} \
} while(0)
int option = 1;
LOCAL_CHECK_ERRNO(setsockopt, s, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
LOCAL_CHECK_ERRNO(setsockopt, s, IPPROTO_TCP, TCP_QUICKACK, &option, sizeof(option));
option = H2O_DEFAULT_HANDSHAKE_TIMEOUT_IN_SECS;
LOCAL_CHECK_ERRNO(setsockopt, s, IPPROTO_TCP, TCP_DEFER_ACCEPT, &option, sizeof(option));
option = DEFAULT_TCP_FASTOPEN_QUEUE_LEN;
LOCAL_CHECK_ERRNO(setsockopt, s, IPPROTO_TCP, TCP_FASTOPEN, &option, sizeof(option));
LOCAL_CHECK_ERRNO(bind, s, iter->ai_addr, iter->ai_addrlen);
LOCAL_CHECK_ERRNO(listen, s, INT_MAX);
ret = s;
break;
#undef LOCAL_CHECK_ERRNO
error:
close(s);
}
freeaddrinfo(res);
return ret;
}
static size_t get_maximum_cache_line_size(void)
{
const int name[] = {_SC_LEVEL1_DCACHE_LINESIZE,
_SC_LEVEL2_CACHE_LINESIZE,
_SC_LEVEL3_CACHE_LINESIZE,
_SC_LEVEL4_CACHE_LINESIZE};
size_t ret = 0;
for (size_t i = 0; i < ARRAY_SIZE(name); i++) {
errno = 0;
const long rc = sysconf(name[i]);
if (rc < 0) {
if (errno)
LIBRARY_ERROR("sysconf");
}
else if ((size_t) rc > ret)
ret = rc;
}
if (!ret)
ret = DEFAULT_CACHE_LINE_SIZE;
return ret;
}
static int initialize_global_data(const config_t *config, global_data_t *global_data)
{
sigset_t signals;
memset(global_data, 0, sizeof(*global_data));
global_data->config = config;
global_data->memory_alignment = get_maximum_cache_line_size();
assert(global_data->memory_alignment <= DEFAULT_CACHE_LINE_SIZE);
CHECK_ERRNO(sigemptyset, &signals);
#ifdef NDEBUG
CHECK_ERRNO(sigaddset, &signals, SIGINT);
#endif // NDEBUG
CHECK_ERRNO(sigaddset, &signals, SIGTERM);
CHECK_ERRNO_RETURN(global_data->signal_fd, signalfd, -1, &signals, SFD_NONBLOCK | SFD_CLOEXEC);
global_data->fortunes_template = get_fortunes_template(config->template_path);
h2o_config_init(&global_data->h2o_config);
global_data->listener_sd = get_listener_socket(config);
if (global_data->listener_sd == -1)
goto error;
if (config->cert && config->key)
initialize_openssl(global_data);
const h2o_iovec_t host = h2o_iovec_init(H2O_STRLIT("default"));
h2o_hostconf_t * const hostconf = h2o_config_register_host(&global_data->h2o_config,
host,
config->port);
h2o_access_log_filehandle_t *log_handle = NULL;
if (config->log) {
log_handle = h2o_access_log_open_handle(config->log, NULL);
if (!log_handle)
goto error;
}
register_request_handlers(hostconf, log_handle);
// Must be registered after the rest of the request handlers.
if (config->root) {
h2o_pathconf_t * const pathconf = h2o_config_register_path(hostconf, "/", 0);
h2o_file_register(pathconf, config->root, NULL, NULL, 0);
if (log_handle)
global_data->file_logger = h2o_access_log_register(pathconf, log_handle);
}
global_data->ctx = initialize_thread_contexts(global_data);
if (global_data->ctx)
return EXIT_SUCCESS;
error:
free_global_data(global_data);
return EXIT_FAILURE;
}
static int parse_options(int argc, char *argv[], config_t *config)
{
memset(config, 0, sizeof(*config));
opterr = 0;
while (1) {
const int opt = getopt(argc, argv, "?a:b:c:d:f:k:l:m:p:q:r:t:");
if (opt == -1)
break;
switch (opt) {
#define PARSE_NUMBER(out) \
do { \
errno = 0; \
\
const long long n = strtoll(optarg, NULL, 10); \
\
if (errno) { \
print_library_error(__FILE__, __LINE__, "strtoll", errno); \
return EXIT_FAILURE; \
} \
\
(out) = n; \
} while(0)
case 'a':
PARSE_NUMBER(config->max_accept);
break;
case 'b':
config->bind_address = optarg;
break;
case 'c':
config->cert = optarg;
break;
case 'd':
config->db_host = optarg;
break;
case 'f':
config->template_path = optarg;
break;
case 'k':
config->key = optarg;
break;
case 'l':
config->log = optarg;
break;
case 'm':
PARSE_NUMBER(config->max_db_conn_num);
break;
case 'p':
PARSE_NUMBER(config->port);
break;
case 'q':
PARSE_NUMBER(config->max_query_num);
break;
case 'r':
config->root = optarg;
break;
case 't':
PARSE_NUMBER(config->thread_num);
break;
default:
fprintf(stderr, USAGE_MESSAGE, *argv);
return EXIT_FAILURE;
#undef PARSE_NUMBER
}
}
set_default_options(config);
return EXIT_SUCCESS;
}
static void set_default_options(config_t *config)
{
if (!config->max_accept)
config->max_accept = 10;
if (!config->max_db_conn_num)
config->max_db_conn_num = 10;
if (!config->max_query_num)
config->max_query_num = 10000;
if (!config->port)
config->port = 8080;
if (!config->thread_num)
config->thread_num = h2o_numproc();
}
static void setup_process(void)
{
sigset_t signals;
CHECK_ERRNO(sigfillset, &signals);
CHECK_ERRNO(sigdelset, &signals, SIGBUS);
CHECK_ERRNO(sigdelset, &signals, SIGFPE);
CHECK_ERRNO(sigdelset, &signals, SIGILL);
CHECK_ERRNO(sigdelset, &signals, SIGSEGV);
#ifndef NDEBUG
CHECK_ERRNO(sigdelset, &signals, SIGINT);
#endif // NDEBUG
CHECK_ERROR(pthread_sigmask, SIG_BLOCK, &signals, NULL);
struct rlimit rlim = {.rlim_cur = 0};
CHECK_ERRNO(getrlimit, RLIMIT_NOFILE, &rlim);
rlim.rlim_cur = rlim.rlim_max;
CHECK_ERRNO(setrlimit, RLIMIT_NOFILE, &rlim);
}
int main(int argc, char *argv[])
{
config_t config;
int rc = EXIT_FAILURE;
if (parse_options(argc, argv, &config) == EXIT_SUCCESS) {
global_data_t global_data;
if (initialize_global_data(&config, &global_data) == EXIT_SUCCESS) {
setup_process();
start_threads(global_data.ctx);
connect_to_database(global_data.ctx);
event_loop(global_data.ctx);
free_global_data(&global_data);
rc = EXIT_SUCCESS;
}
}
return rc;
}
View
@@ -0,0 +1,255 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 <assert.h>
#include <h2o.h>
#include <stdalign.h>
#include <string.h>
#include <yajl/yajl_gen.h>
#include "error.h"
#include "fortune.h"
#include "request_handler.h"
#include "utility.h"
#include "world.h"
typedef struct {
yajl_gen gen;
h2o_generator_t h2o_generator;
} json_ctx_t;
static void cleanup_json_response(struct st_h2o_generator_t *self, h2o_req_t *req);
static void complete_json_response(struct st_h2o_generator_t *self, h2o_req_t *req);
static int json_serializer(struct st_h2o_handler_t *self, h2o_req_t *req);
static int plaintext(struct st_h2o_handler_t *self, h2o_req_t *req);
static const char *status_code_to_string(http_status_code_t status_code);
static void cleanup_json_response(struct st_h2o_generator_t *self, h2o_req_t *req)
{
IGNORE_FUNCTION_PARAMETER(req);
json_ctx_t * const json_ctx = H2O_STRUCT_FROM_MEMBER(json_ctx_t, h2o_generator, self);
yajl_gen_free(json_ctx->gen);
}
static void complete_json_response(struct st_h2o_generator_t *self, h2o_req_t *req)
{
h2o_send(req, NULL, 0, true);
cleanup_json_response(self, req);
}
static int json_serializer(struct st_h2o_handler_t *self, h2o_req_t *req)
{
IGNORE_FUNCTION_PARAMETER(self);
json_ctx_t * const json_ctx = h2o_mem_alloc_pool(&req->pool, sizeof(*json_ctx));
if (json_ctx) {
memset(json_ctx, 0, sizeof(*json_ctx));
json_ctx->gen = get_json_generator(&req->pool);
json_ctx->h2o_generator.proceed = complete_json_response;
json_ctx->h2o_generator.stop = cleanup_json_response;
if (json_ctx->gen) {
CHECK_YAJL_STATUS(yajl_gen_map_open, json_ctx->gen);
CHECK_YAJL_STATUS(yajl_gen_string, json_ctx->gen, YAJL_STRLIT("message"));
CHECK_YAJL_STATUS(yajl_gen_string, json_ctx->gen, YAJL_STRLIT("Hello, World!"));
CHECK_YAJL_STATUS(yajl_gen_map_close, json_ctx->gen);
if (!send_json_response(json_ctx->gen, &json_ctx->h2o_generator, req))
return 0;
error_yajl:
yajl_gen_free(json_ctx->gen);
}
}
send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
return 0;
}
static int plaintext(struct st_h2o_handler_t *self, h2o_req_t *req)
{
IGNORE_FUNCTION_PARAMETER(self);
set_default_response_param(PLAIN, req);
h2o_send_inline(req, H2O_STRLIT("Hello, World!"));
return 0;
}
static const char *status_code_to_string(http_status_code_t status_code)
{
const char *ret;
switch (status_code) {
case BAD_GATEWAY:
ret = "Bad Gateway";
break;
case GATEWAY_TIMEOUT:
ret = "Gateway Timeout";
break;
case INTERNAL_SERVER_ERROR:
ret = "Internal Server Error";
break;
case OK:
ret = "OK";
break;
case SERVICE_UNAVAILABLE:
ret = "Service Unavailable";
break;
default:
ret = "";
}
return ret;
}
const char *get_query_param(const char *query,
size_t query_len,
const char *param,
size_t param_len)
{
const char *ret = NULL;
while (param_len < query_len) {
if (!memcmp(param, query, param_len)) {
ret = query + param_len;
break;
}
const char * const next = memchr(query, '&', query_len);
if (!next)
break;
query_len -= next + 1 - query;
query = next + 1;
}
return ret;
}
void register_request_handlers(h2o_hostconf_t *hostconf, h2o_access_log_filehandle_t *log_handle)
{
h2o_pathconf_t *pathconf = h2o_config_register_path(hostconf, "/json", 0);
h2o_handler_t *handler = h2o_create_handler(pathconf, sizeof(*handler));
if (log_handle)
h2o_access_log_register(pathconf, log_handle);
handler->on_req = json_serializer;
pathconf = h2o_config_register_path(hostconf, "/db", 0);
handler = h2o_create_handler(pathconf, sizeof(*handler));
handler->on_req = single_query;
if (log_handle)
h2o_access_log_register(pathconf, log_handle);
pathconf = h2o_config_register_path(hostconf, "/queries", 0);
handler = h2o_create_handler(pathconf, sizeof(*handler));
handler->on_req = multiple_queries;
if (log_handle)
h2o_access_log_register(pathconf, log_handle);
pathconf = h2o_config_register_path(hostconf, "/fortunes", 0);
handler = h2o_create_handler(pathconf, sizeof(*handler));
handler->on_req = fortunes;
if (log_handle)
h2o_access_log_register(pathconf, log_handle);
pathconf = h2o_config_register_path(hostconf, "/updates", 0);
handler = h2o_create_handler(pathconf, sizeof(*handler));
handler->on_req = updates;
if (log_handle)
h2o_access_log_register(pathconf, log_handle);
pathconf = h2o_config_register_path(hostconf, "/plaintext", 0);
handler = h2o_create_handler(pathconf, sizeof(*handler));
handler->on_req = plaintext;
if (log_handle)
h2o_access_log_register(pathconf, log_handle);
}
void send_error(http_status_code_t status_code, const char *body, h2o_req_t *req)
{
h2o_send_error_generic(req, status_code, status_code_to_string(status_code), body, 0);
}
int send_json_response(yajl_gen gen, h2o_generator_t *h2o_generator, h2o_req_t *req)
{
h2o_iovec_t h2o_iovec = {.len = 0};
int ret = EXIT_FAILURE;
static_assert(sizeof(h2o_iovec.base) == sizeof(const unsigned char *) &&
alignof(h2o_iovec.base) == alignof(const unsigned char *),
"Types must be compatible.");
if (yajl_gen_get_buf(gen,
(const unsigned char **) &h2o_iovec.base,
&h2o_iovec.len) == yajl_gen_status_ok) {
set_default_response_param(JSON, req);
h2o_start_response(req, h2o_generator);
h2o_send(req, &h2o_iovec, 1, false);
ret = EXIT_SUCCESS;
}
return ret;
}
void send_service_unavailable_error(const char *body, h2o_req_t *req)
{
h2o_add_header(&req->pool,
&req->res.headers,
H2O_TOKEN_RETRY_AFTER,
H2O_STRLIT(MKSTR(H2O_DEFAULT_HTTP1_REQ_TIMEOUT_IN_SECS)));
h2o_send_error_503(req,
status_code_to_string(SERVICE_UNAVAILABLE),
body,
H2O_SEND_ERROR_KEEP_HEADERS);
}
void set_default_response_param(content_type_t content_type, h2o_req_t *req)
{
req->res.status = OK;
req->res.reason = status_code_to_string(req->res.status);
switch (content_type) {
case JSON:
h2o_add_header(&req->pool,
&req->res.headers,
H2O_TOKEN_CONTENT_TYPE,
H2O_STRLIT("application/json"));
break;
case PLAIN:
h2o_add_header(&req->pool,
&req->res.headers,
H2O_TOKEN_CONTENT_TYPE,
H2O_STRLIT("text/plain"));
break;
default:
h2o_add_header(&req->pool,
&req->res.headers,
H2O_TOKEN_CONTENT_TYPE,
H2O_STRLIT("text/html; charset=utf-8"));
}
}
View
@@ -0,0 +1,51 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 REQUEST_H_
#define REQUEST_H_
#include <h2o.h>
#include <yajl/yajl_gen.h>
typedef enum {
HTML,
JSON,
PLAIN
} content_type_t;
typedef enum {
OK = 200,
INTERNAL_SERVER_ERROR = 500,
BAD_GATEWAY = 502,
SERVICE_UNAVAILABLE = 503,
GATEWAY_TIMEOUT = 504
} http_status_code_t;
const char *get_query_param(const char *query,
size_t query_len,
const char *param,
size_t param_len);
void register_request_handlers(h2o_hostconf_t *hostconf, h2o_access_log_filehandle_t *log_handle);
void send_error(http_status_code_t status_code, const char *body, h2o_req_t *req);
int send_json_response(yajl_gen gen, h2o_generator_t *h2o_generator, h2o_req_t *req);
void send_service_unavailable_error(const char *body, h2o_req_t *req);
void set_default_response_param(content_type_t content_type, h2o_req_t *req);
#endif // REQUEST_H_
View
@@ -0,0 +1,138 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mustache.h>
#include "database.h"
#include "error.h"
#include "fortune.h"
#include "template.h"
#include "utility.h"
typedef struct {
FILE *input;
const char *name;
} template_input_t;
static uintmax_t prerender_section(mustache_api_t *api,
void *userdata,
mustache_token_section_t *token);
static uintmax_t prerender_variable(mustache_api_t *api,
void *userdata,
mustache_token_variable_t *token);
static uintmax_t read_template(mustache_api_t *api,
void *userdata,
char *buffer,
uintmax_t buffer_size);
static void template_error(mustache_api_t *api, void *userdata, uintmax_t lineno, char *error);
static uintmax_t prerender_section(mustache_api_t *api,
void *userdata,
mustache_token_section_t *token)
{
bool * const in_section = userdata;
uintmax_t ret = 0;
if (!*in_section && !strcmp(token->name, FORTUNE_TABLE_NAME)) {
*in_section = true;
ret = mustache_prerender(api, userdata, token->section);
*in_section = false;
}
return ret;
}
static uintmax_t prerender_variable(mustache_api_t *api,
void *userdata,
mustache_token_variable_t *token)
{
IGNORE_FUNCTION_PARAMETER(api);
bool * const in_section = userdata;
uintmax_t ret = 0;
if (*in_section) {
if (token->text_length == sizeof(ID_FIELD_NAME) - 1 &&
!memcmp(token->text, ID_FIELD_NAME, sizeof(ID_FIELD_NAME) - 1)) {
token->userdata = (void *) offsetof(fortune_t, id);
ret = 1;
}
else if (token->text_length == sizeof(MESSAGE_FIELD_NAME) - 1 &&
!memcmp(token->text, MESSAGE_FIELD_NAME, sizeof(MESSAGE_FIELD_NAME) - 1)) {
token->userdata = (void *) offsetof(fortune_t, message);
ret = 1;
}
}
return ret;
}
static uintmax_t read_template(mustache_api_t *api,
void *userdata,
char *buffer,
uintmax_t buffer_size)
{
IGNORE_FUNCTION_PARAMETER(api);
const template_input_t * const template_input = userdata;
return fread(buffer, sizeof(*buffer), buffer_size, template_input->input);
}
static void template_error(mustache_api_t *api, void *userdata, uintmax_t lineno, char *error)
{
IGNORE_FUNCTION_PARAMETER(api);
const template_input_t * const template_input = userdata;
print_error(template_input->name, lineno, "mustache_compile", error);
}
mustache_template_t *get_fortunes_template(const char *path)
{
mustache_template_t *ret = NULL;
template_input_t template_input = {.input = fopen(path, "rb"), .name = path};
if (template_input.input) {
mustache_api_t api = {.error = template_error,
.read = read_template,
.sectget = prerender_section,
.varget = prerender_variable};
bool in_section = false;
ret = mustache_compile(&api, &template_input);
if (ret && !mustache_prerender(&api, &in_section, ret)) {
mustache_free(&api, ret);
ret = NULL;
}
fclose(template_input.input);
}
else
LIBRARY_ERROR("fopen");
return ret;
}
View
@@ -0,0 +1,28 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 TEMPLATE_H_
#define TEMPLATE_H_
#include <mustache.h>
mustache_template_t *get_fortunes_template(const char *path);
#endif // TEMPLATE_H_
View
@@ -0,0 +1,107 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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.
*/
#define _GNU_SOURCE
#include <errno.h>
#include <h2o.h>
#include <pthread.h>
#include <stdlib.h>
#include <h2o/serverutil.h>
#include <sys/epoll.h>
#include "database.h"
#include "error.h"
#include "event_loop.h"
#include "thread.h"
static void *run_thread(void *arg);
static void *run_thread(void *arg)
{
connect_to_database(arg);
event_loop(arg);
pthread_exit(NULL);
}
void free_thread_contexts(global_data_t *global_data)
{
thread_context_t * const ctx = global_data->ctx;
for (size_t i = 0; i < ctx->global_data->config->thread_num; i++) {
if (i)
CHECK_ERROR(pthread_join, ctx[i].thread, NULL);
else
// Even though this is global data, we need to close
// it before the associated event loop is cleaned up.
h2o_socket_close(global_data->signals);
free_database_state(ctx[i].event_loop.h2o_ctx.loop, &ctx[i].db_state);
free_event_loop(&ctx[i].event_loop);
}
free(ctx);
}
thread_context_t *initialize_thread_contexts(global_data_t *global_data)
{
const size_t sz = global_data->config->thread_num * sizeof(thread_context_t);
thread_context_t * const ret = aligned_alloc(global_data->memory_alignment, sz);
if (ret) {
memset(ret, 0, sz);
for (size_t i = 0; i < global_data->config->thread_num; i++) {
ret[i].global_data = global_data;
initialize_event_loop(!i, global_data, &ret[i].event_loop);
initialize_database_state(ret[i].event_loop.h2o_ctx.loop, &ret[i].db_state);
}
}
return ret;
}
void start_threads(thread_context_t *ctx)
{
const size_t num_cpus = h2o_numproc();
// The first thread context is used by the main thread.
ctx->thread = pthread_self();
for (size_t i = 1; i < ctx->global_data->config->thread_num; i++)
CHECK_ERROR(pthread_create, &ctx[i].thread, NULL, run_thread, ctx + i);
// If the number of threads is not equal to the number of processors, then let the scheduler
// decide how to balance the load.
if (ctx->global_data->config->thread_num == num_cpus) {
const size_t cpusetsize = CPU_ALLOC_SIZE(num_cpus);
cpu_set_t * const cpuset = CPU_ALLOC(num_cpus);
if (!cpuset)
abort();
for (size_t i = 0; i < ctx->global_data->config->thread_num; i++) {
CPU_ZERO_S(cpusetsize, cpuset);
CPU_SET_S(i, cpusetsize, cpuset);
CHECK_ERROR(pthread_setaffinity_np, ctx[i].thread, cpusetsize, cpuset);
}
CPU_FREE(cpuset);
}
}
View
@@ -0,0 +1,56 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 THREAD_H_
#define THREAD_H_
#include <assert.h>
#include <h2o.h>
#include <pthread.h>
#include <sys/types.h>
#include "database.h"
#include "event_loop.h"
#include "utility.h"
#define DEFAULT_CACHE_LINE_SIZE 64
typedef struct thread_context_t thread_context_t;
struct thread_context_t {
global_data_t *global_data;
unsigned random_seed;
pid_t tid;
db_state_t db_state;
event_loop_t event_loop;
pthread_t thread;
// Align on the cache line size to prevent false sharing.
char padding[49];
};
static_assert(!(sizeof(thread_context_t) % DEFAULT_CACHE_LINE_SIZE),
"The size of the thread_context_t structure must be a "
"multiple of the cache line size.");
void free_thread_contexts(global_data_t *global_data);
thread_context_t *initialize_thread_contexts(global_data_t *global_data);
void start_threads(thread_context_t *ctx);
#endif // THREAD_H_
View
@@ -0,0 +1,169 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 <assert.h>
#include <pthread.h>
#include <stddef.h>
#include <stdlib.h>
#include <openssl/conf.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include "error.h"
#include "tls.h"
#include "utility.h"
#define CHECK_OPENSSL_ERROR(function, ...) \
do { \
const int error_code = (function)(__VA_ARGS__); \
\
if (error_code != 1) { \
const unsigned long openssl_error = ERR_get_error(); \
char buf[128] = ""; \
\
ERR_error_string_n(openssl_error, buf, sizeof(buf)); \
print_error(__FILE__, __LINE__, #function, "%s (%lu)", buf, openssl_error); \
abort(); \
} \
} while(0)
struct CRYPTO_dynlock_value {
pthread_mutex_t mutex;
};
static struct CRYPTO_dynlock_value *dyn_create_function(const char *file, int line);
static void dyn_destroy_function(struct CRYPTO_dynlock_value *l, const char *file, int line);
static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line);
static void locking_function(int mode, int n, const char *file, int line);
static struct {
pthread_mutex_t *lock;
size_t num_lock;
pthread_mutexattr_t lock_attr;
} openssl_global_data;
static struct CRYPTO_dynlock_value *dyn_create_function(const char *file, int line)
{
struct CRYPTO_dynlock_value *ret = malloc(sizeof(*ret));
if (ret) {
const int error_code = pthread_mutex_init(&ret->mutex, &openssl_global_data.lock_attr);
if (error_code) {
print_library_error(file, line, "pthread_mutex_init", error_code);
free(ret);
ret = NULL;
}
}
return ret;
}
static void dyn_destroy_function(struct CRYPTO_dynlock_value *l, const char *file, int line)
{
const int error_code = pthread_mutex_destroy(&l->mutex);
if (error_code)
print_library_error(file, line, "pthread_mutex_destroy", error_code);
free(l);
}
static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line)
{
const char *function;
int error_code;
if (mode & CRYPTO_LOCK) {
function = "pthread_mutex_lock";
error_code = pthread_mutex_lock(&l->mutex);
}
else {
function = "pthread_mutex_unlock";
error_code = pthread_mutex_unlock(&l->mutex);
}
if (error_code) {
print_library_error(file, line, function, error_code);
abort();
}
}
static void locking_function(int mode, int n, const char *file, int line)
{
assert((size_t) n < openssl_global_data.num_lock);
static_assert(!offsetof(struct CRYPTO_dynlock_value, mutex),
"The mutex must be the first field in struct CRYPTO_dynlock_value.");
dyn_lock_function(mode, (struct CRYPTO_dynlock_value *) (openssl_global_data.lock + n), file, line);
}
void cleanup_openssl(global_data_t *global_data)
{
SSL_CTX_free(global_data->ssl_ctx);
CRYPTO_set_locking_callback(NULL);
CRYPTO_set_id_callback(NULL);
CRYPTO_set_dynlock_create_callback(NULL);
CRYPTO_set_dynlock_destroy_callback(NULL);
CRYPTO_set_dynlock_lock_callback(NULL);
ERR_remove_state(0);
ERR_free_strings();
CONF_modules_unload(1);
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
for (size_t i = 0; i < openssl_global_data.num_lock; i++)
CHECK_ERROR(pthread_mutex_destroy, openssl_global_data.lock + i);
free(openssl_global_data.lock);
CHECK_ERROR(pthread_mutexattr_destroy, &openssl_global_data.lock_attr);
}
void initialize_openssl(global_data_t *global_data)
{
SSL_library_init();
SSL_load_error_strings();
openssl_global_data.num_lock = CRYPTO_num_locks();
openssl_global_data.lock = malloc(openssl_global_data.num_lock *
sizeof(*openssl_global_data.lock));
CHECK_ERROR(pthread_mutexattr_init, &openssl_global_data.lock_attr);
CHECK_ERROR(pthread_mutexattr_settype,
&openssl_global_data.lock_attr,
PTHREAD_MUTEX_ADAPTIVE_NP);
for (size_t i = 0; i < openssl_global_data.num_lock; i++)
CHECK_ERROR(pthread_mutex_init,
openssl_global_data.lock + i,
&openssl_global_data.lock_attr);
CRYPTO_set_locking_callback(locking_function);
CRYPTO_set_dynlock_create_callback(dyn_create_function);
CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function);
CRYPTO_set_dynlock_lock_callback(dyn_lock_function);
global_data->ssl_ctx = SSL_CTX_new(TLSv1_2_server_method());
CHECK_OPENSSL_ERROR(SSL_CTX_use_certificate_file,
global_data->ssl_ctx,
global_data->config->cert,
SSL_FILETYPE_PEM);
CHECK_OPENSSL_ERROR(SSL_CTX_use_PrivateKey_file,
global_data->ssl_ctx,
global_data->config->key,
SSL_FILETYPE_PEM);
}
View
@@ -0,0 +1,29 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 TLS_H_
#define TLS_H_
#include "utility.h"
void cleanup_openssl(global_data_t *global_data);
void initialize_openssl(global_data_t *global_data);
#endif // TLS_H_
View
@@ -0,0 +1,95 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 <assert.h>
#include <h2o.h>
#include <stdalign.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <yajl/yajl_gen.h>
#include "utility.h"
static void mem_pool_free(void *ctx, void *ptr);
static void *mem_pool_malloc(void *ctx, size_t sz);
static void *mem_pool_realloc(void *ctx, void *ptr, size_t sz);
static void mem_pool_free(void *ctx, void *ptr)
{
// The memory pool will free all allocations in one go.
IGNORE_FUNCTION_PARAMETER(ctx);
IGNORE_FUNCTION_PARAMETER(ptr);
}
static void *mem_pool_malloc(void *ctx, size_t sz)
{
size_t * const p = h2o_mem_alloc_pool(ctx, sz + sizeof(*p));
void * const ret = p + 1;
*p = sz;
// check alignment
assert(!(((uintptr_t) ret) & (alignof(void *) - 1)));
return ret;
}
static void *mem_pool_realloc(void *ctx, void *ptr, size_t sz)
{
void *ret;
if (ptr) {
const size_t old_sz = ((const size_t *) ptr)[-1];
if (sz > old_sz) {
ret = mem_pool_malloc(ctx, sz);
memcpy(ret, ptr, old_sz);
}
else
ret = ptr;
}
else
ret = mem_pool_malloc(ctx, sz);
return ret;
}
yajl_gen get_json_generator(h2o_mem_pool_t *pool)
{
const yajl_alloc_funcs mem_pool_alloc_funcs = {mem_pool_malloc,
mem_pool_realloc,
mem_pool_free,
pool};
return yajl_gen_alloc(&mem_pool_alloc_funcs);
}
uint32_t get_random_number(uint32_t max_rand, unsigned int *seed)
{
// In general, RAND_MAX + 1 is not a multiple of max_rand,
// so rand_r() % max_rand would be biased.
const unsigned bucket_size = (RAND_MAX + 1U) / max_rand;
const unsigned unbiased_rand_max = bucket_size * max_rand;
unsigned ret;
do
ret = rand_r(seed);
while (ret >= unbiased_rand_max);
return ret / bucket_size;
}
View
@@ -0,0 +1,73 @@
/*
Copyright (c) 2016 Anton Valentinov Kirilov
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 UTILITY_H_
#define UTILITY_H_
#include <h2o.h>
#include <stdint.h>
#include <openssl/ssl.h>
#include <stdbool.h>
#include <yajl/yajl_gen.h>
#include <mustache.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
// mainly used to silence compiler warnings about unused function parameters
#define IGNORE_FUNCTION_PARAMETER(p) ((void) (p))
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define MKSTR(x) TOSTRING(x)
#define TOSTRING(x) # x
#define YAJL_STRLIT(s) (const unsigned char *) (s), sizeof(s) - 1
typedef struct thread_context_t thread_context_t;
typedef struct {
const char *bind_address;
const char *cert;
const char *db_host;
const char *key;
const char *log;
const char *root;
const char *template_path;
size_t max_accept;
size_t max_db_conn_num;
size_t max_query_num;
size_t thread_num;
uint16_t port;
} config_t;
typedef struct {
const config_t *config;
thread_context_t *ctx;
h2o_logger_t *file_logger;
mustache_template_t *fortunes_template;
h2o_socket_t *signals;
SSL_CTX *ssl_ctx;
size_t memory_alignment;
int listener_sd;
int signal_fd;
bool shutdown;
h2o_globalconf_t h2o_config;
} global_data_t;
yajl_gen get_json_generator(h2o_mem_pool_t *pool);
uint32_t get_random_number(uint32_t max_rand, unsigned int *seed);
#endif // UTILITY_H_
Oops, something went wrong.