From 85959785600653ad83fbc507364269a7792e2404 Mon Sep 17 00:00:00 2001 From: Albert S Date: Wed, 18 Mar 2020 22:15:06 +0100 Subject: [PATCH] get header-only library dependencies using git submodules --- .gitmodules | 6 + Makefile | 4 +- database/hdr/sqlite_modern_cpp.h | 1160 ----- database/hdr/sqlite_modern_cpp/errors.h | 100 - .../hdr/sqlite_modern_cpp/lists/error_codes.h | 90 - database/hdr/sqlite_modern_cpp/log.h | 124 - database/hdr/sqlite_modern_cpp/sqlcipher.h | 53 - .../utility/function_traits.h | 40 - .../utility/uncaught_exceptions.h | 34 - .../sqlite_modern_cpp/utility/utf16_utf8.h | 45 - .../hdr/sqlite_modern_cpp/utility/variant.h | 230 - gateway/httpgateway.h | 2 +- gateway/httplib.h | 4431 ----------------- submodules/cpp-httplib | 1 + submodules/sqlitemoderncpp | 1 + 15 files changed, 11 insertions(+), 6310 deletions(-) create mode 100644 .gitmodules delete mode 100644 database/hdr/sqlite_modern_cpp.h delete mode 100644 database/hdr/sqlite_modern_cpp/errors.h delete mode 100644 database/hdr/sqlite_modern_cpp/lists/error_codes.h delete mode 100644 database/hdr/sqlite_modern_cpp/log.h delete mode 100644 database/hdr/sqlite_modern_cpp/sqlcipher.h delete mode 100644 database/hdr/sqlite_modern_cpp/utility/function_traits.h delete mode 100644 database/hdr/sqlite_modern_cpp/utility/uncaught_exceptions.h delete mode 100644 database/hdr/sqlite_modern_cpp/utility/utf16_utf8.h delete mode 100644 database/hdr/sqlite_modern_cpp/utility/variant.h delete mode 100644 gateway/httplib.h create mode 160000 submodules/cpp-httplib create mode 160000 submodules/sqlitemoderncpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..111d41b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "submodules/sqlitemoderncpp"] + path = submodules/sqlitemoderncpp + url = https://github.com/SqliteModernCpp/sqlite_modern_cpp +[submodule "submodules/cpp-httplib"] + path = submodules/cpp-httplib + url = https://github.com/yhirose/cpp-httplib diff --git a/Makefile b/Makefile index 9960bf3..9e18149 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ GTEST_OBJECTS=$(filter-out qswiki.o, $(WIKIOBJECTS)) release: CXXFLAGS=$(RELEASE_CXXFLAGS) release: qswiki qswiki: $(WIKIOBJECTS) - $(CXX) $(WIKIOBJECTS) ${LDFLAGS} -I database/hdr -o qswiki + $(CXX) $(WIKIOBJECTS) ${LDFLAGS} -I submodules/sqlitemoderncpp/hdr -I submodules/cpp-httplib -o qswiki test: $(TESTOBJECTS) $(CXX) $(TESTOBJECTS) ${LDFLAGS} -o test @@ -52,7 +52,7 @@ gtest: $(GTESTS_TESTDIR)/*.cpp $(GTEST_OBJECTS) $(CXX) -o gtest $(GTESTS_TESTDIR)/*.cpp $(GTEST_OBJECTS) $(GTEST_CXXFLAGS) $(GTEST_DIR)/src/gtest_main.cc $(GTEST_DIR)/src/gtest-all.cc $(GTEST_LDFLAGS) %.o:%.cpp - $(CXX) ${CXXFLAGS} ${LDFLAGS} -I database/hdr -c -o $@ $< + $(CXX) ${CXXFLAGS} ${LDFLAGS} -I submodules/sqlitemoderncpp/hdr -I submodules/cpp-httplib -c -o $@ $< clean: rm -f $(OBJECTS) $(DEPENDS) diff --git a/database/hdr/sqlite_modern_cpp.h b/database/hdr/sqlite_modern_cpp.h deleted file mode 100644 index 238b9d8..0000000 --- a/database/hdr/sqlite_modern_cpp.h +++ /dev/null @@ -1,1160 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#define MODERN_SQLITE_VERSION 3002008 - -#ifdef __has_include -#if __cplusplus > 201402 && __has_include() -#define MODERN_SQLITE_STD_OPTIONAL_SUPPORT -#elif __has_include() -#define MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT -#endif -#endif - -#ifdef __has_include -#if __cplusplus > 201402 && __has_include() -#define MODERN_SQLITE_STD_VARIANT_SUPPORT -#endif -#endif - -#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT -#include -#endif - -#ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT -#include -#define MODERN_SQLITE_STD_OPTIONAL_SUPPORT -#endif - -#ifdef _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT -#include -#endif - -#include - -#include "sqlite_modern_cpp/errors.h" -#include "sqlite_modern_cpp/utility/function_traits.h" -#include "sqlite_modern_cpp/utility/uncaught_exceptions.h" -#include "sqlite_modern_cpp/utility/utf16_utf8.h" - -#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT -#include "sqlite_modern_cpp/utility/variant.h" -#endif - -namespace sqlite -{ - -// std::optional support for NULL values -#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT -#ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT -template using optional = std::experimental::optional; -#else -template using optional = std::optional; -#endif -#endif - -class database; -class database_binder; - -template class binder; - -typedef std::shared_ptr connection_type; - -template ::value == Element)> struct tuple_iterate -{ - static void iterate(Tuple &t, database_binder &db) - { - get_col_from_db(db, Element, std::get(t)); - tuple_iterate::iterate(t, db); - } -}; - -template struct tuple_iterate -{ - static void iterate(Tuple &, database_binder &) - { - } -}; - -class database_binder -{ - - public: - // database_binder is not copyable - database_binder() = delete; - database_binder(const database_binder &other) = delete; - database_binder &operator=(const database_binder &) = delete; - - database_binder(database_binder &&other) - : _db(std::move(other._db)), _stmt(std::move(other._stmt)), _inx(other._inx), - execution_started(other.execution_started) - { - } - - void execute() - { - _start_execute(); - int hresult; - - while((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) - { - } - - if(hresult != SQLITE_DONE) - { - errors::throw_sqlite_error(hresult, sql()); - } - } - - std::string sql() - { -#if SQLITE_VERSION_NUMBER >= 3014000 - auto sqlite_deleter = [](void *ptr) { sqlite3_free(ptr); }; - std::unique_ptr str(sqlite3_expanded_sql(_stmt.get()), sqlite_deleter); - return str ? str.get() : original_sql(); -#else - return original_sql(); -#endif - } - - std::string original_sql() - { - return sqlite3_sql(_stmt.get()); - } - - void used(bool state) - { - if(!state) - { - // We may have to reset first if we haven't done so already: - _next_index(); - --_inx; - } - execution_started = state; - } - bool used() const - { - return execution_started; - } - - private: - std::shared_ptr _db; - std::unique_ptr _stmt; - utility::UncaughtExceptionDetector _has_uncaught_exception; - - int _inx; - - bool execution_started = false; - - int _next_index() - { - if(execution_started && !_inx) - { - sqlite3_reset(_stmt.get()); - sqlite3_clear_bindings(_stmt.get()); - } - return ++_inx; - } - void _start_execute() - { - _next_index(); - _inx = 0; - used(true); - } - - void _extract(std::function call_back) - { - int hresult; - _start_execute(); - - while((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) - { - call_back(); - } - - if(hresult != SQLITE_DONE) - { - errors::throw_sqlite_error(hresult, sql()); - } - } - - void _extract_single_value(std::function call_back) - { - int hresult; - _start_execute(); - - if((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) - { - call_back(); - } - else if(hresult == SQLITE_DONE) - { - throw errors::no_rows("no rows to extract: exactly 1 row expected", sql(), SQLITE_DONE); - } - - if((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) - { - throw errors::more_rows("not all rows extracted", sql(), SQLITE_ROW); - } - - if(hresult != SQLITE_DONE) - { - errors::throw_sqlite_error(hresult, sql()); - } - } - - sqlite3_stmt *_prepare(const std::u16string &sql) - { - return _prepare(utility::utf16_to_utf8(sql)); - } - - sqlite3_stmt *_prepare(const std::string &sql) - { - int hresult; - sqlite3_stmt *tmp = nullptr; - const char *remaining; - hresult = sqlite3_prepare_v2(_db.get(), sql.data(), -1, &tmp, &remaining); - if(hresult != SQLITE_OK) - errors::throw_sqlite_error(hresult, sql); - if(!std::all_of(remaining, sql.data() + sql.size(), [](char ch) { return std::isspace(ch); })) - throw errors::more_statements("Multiple semicolon separated statements are unsupported", sql); - return tmp; - } - - template - struct is_sqlite_value - : public std::integral_constant::value || std::is_integral::value || - std::is_same::value || - std::is_same::value || - std::is_same::value> - { - }; - template - struct is_sqlite_value> - : public std::integral_constant::value || std::is_integral::value || - std::is_same::value> - { - }; -#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT - template - struct is_sqlite_value> : public std::integral_constant - { - }; -#endif - - /* for vector support */ - template - friend database_binder &operator<<(database_binder &db, const std::vector &val); - template friend void get_col_from_db(database_binder &db, int inx, std::vector &val); - /* for nullptr & unique_ptr support */ - friend database_binder &operator<<(database_binder &db, std::nullptr_t); - template friend database_binder &operator<<(database_binder &db, const std::unique_ptr &val); - template friend void get_col_from_db(database_binder &db, int inx, std::unique_ptr &val); -#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT - template - friend database_binder &operator<<(database_binder &db, const std::variant &val); - template friend void get_col_from_db(database_binder &db, int inx, std::variant &val); -#endif - template friend T operator++(database_binder &db, int); - // Overload instead of specializing function templates (http://www.gotw.ca/publications/mill17.htm) - friend database_binder &operator<<(database_binder &db, const int &val); - friend void get_col_from_db(database_binder &db, int inx, int &val); - friend database_binder &operator<<(database_binder &db, const sqlite_int64 &val); - friend void get_col_from_db(database_binder &db, int inx, sqlite3_int64 &i); - friend database_binder &operator<<(database_binder &db, const float &val); - friend void get_col_from_db(database_binder &db, int inx, float &f); - friend database_binder &operator<<(database_binder &db, const double &val); - friend void get_col_from_db(database_binder &db, int inx, double &d); - friend void get_col_from_db(database_binder &db, int inx, std::string &s); - friend database_binder &operator<<(database_binder &db, const std::string &txt); - friend void get_col_from_db(database_binder &db, int inx, std::u16string &w); - friend database_binder &operator<<(database_binder &db, const std::u16string &txt); - -#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT - template - friend database_binder &operator<<(database_binder &db, const optional &val); - template friend void get_col_from_db(database_binder &db, int inx, optional &o); -#endif - -#ifdef _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT - template - friend database_binder &operator<<(database_binder &db, const boost::optional &val); - template - friend void get_col_from_db(database_binder &db, int inx, boost::optional &o); -#endif - - public: - database_binder(std::shared_ptr db, std::u16string const &sql) - : _db(db), _stmt(_prepare(sql), sqlite3_finalize), _inx(0) - { - } - - database_binder(std::shared_ptr db, std::string const &sql) - : _db(db), _stmt(_prepare(sql), sqlite3_finalize), _inx(0) - { - } - - ~database_binder() noexcept(false) - { - /* Will be executed if no >>op is found, but not if an exception - is in mid flight */ - if(!used() && !_has_uncaught_exception && _stmt) - { - execute(); - } - } - - template - typename std::enable_if::value, void>::type operator>>(Result &value) - { - this->_extract_single_value([&value, this] { get_col_from_db(*this, 0, value); }); - } - - template void operator>>(std::tuple &&values) - { - this->_extract_single_value([&values, this] { tuple_iterate>::iterate(values, *this); }); - } - - template - typename std::enable_if::value, void>::type operator>>(Function &&func) - { - typedef utility::function_traits traits; - - this->_extract([&func, this]() { binder::run(*this, func); }); - } -}; - -namespace sql_function_binder -{ -template -inline void step(sqlite3_context *db, int count, sqlite3_value **vals); - -template -inline typename std::enable_if<(sizeof...(Values) && sizeof...(Values) < Count), void>::type step(sqlite3_context *db, - int count, - sqlite3_value **vals, - Values &&...values); - -template -inline typename std::enable_if<(sizeof...(Values) == Count), void>::type step(sqlite3_context *db, int, - sqlite3_value **, Values &&...values); - -template inline void final(sqlite3_context *db); - -template -inline typename std::enable_if<(sizeof...(Values) < Count), void>::type scalar(sqlite3_context *db, int count, - sqlite3_value **vals, - Values &&...values); - -template -inline typename std::enable_if<(sizeof...(Values) == Count), void>::type scalar(sqlite3_context *db, int, - sqlite3_value **, Values &&...values); -} // namespace sql_function_binder - -enum class OpenFlags -{ - READONLY = SQLITE_OPEN_READONLY, - READWRITE = SQLITE_OPEN_READWRITE, - CREATE = SQLITE_OPEN_CREATE, - NOMUTEX = SQLITE_OPEN_NOMUTEX, - FULLMUTEX = SQLITE_OPEN_FULLMUTEX, - SHAREDCACHE = SQLITE_OPEN_SHAREDCACHE, - PRIVATECACH = SQLITE_OPEN_PRIVATECACHE, - URI = SQLITE_OPEN_URI -}; -inline OpenFlags operator|(const OpenFlags &a, const OpenFlags &b) -{ - return static_cast(static_cast(a) | static_cast(b)); -} -enum class Encoding -{ - ANY = SQLITE_ANY, - UTF8 = SQLITE_UTF8, - UTF16 = SQLITE_UTF16 -}; -struct sqlite_config -{ - OpenFlags flags = OpenFlags::READWRITE | OpenFlags::CREATE; - const char *zVfs = nullptr; - Encoding encoding = Encoding::ANY; -}; - -class database -{ - protected: - std::shared_ptr _db; - - public: - database(const std::string &db_name, const sqlite_config &config = {}) : _db(nullptr) - { - sqlite3 *tmp = nullptr; - auto ret = sqlite3_open_v2(db_name.data(), &tmp, static_cast(config.flags), config.zVfs); - _db = std::shared_ptr(tmp, [=](sqlite3 *ptr) { - sqlite3_close_v2(ptr); - }); // this will close the connection eventually when no longer needed. - if(ret != SQLITE_OK) - errors::throw_sqlite_error(_db ? sqlite3_extended_errcode(_db.get()) : ret); - sqlite3_extended_result_codes(_db.get(), true); - if(config.encoding == Encoding::UTF16) - *this << R"(PRAGMA encoding = "UTF-16";)"; - } - - database(const std::u16string &db_name, const sqlite_config &config = {}) : _db(nullptr) - { - auto db_name_utf8 = utility::utf16_to_utf8(db_name); - sqlite3 *tmp = nullptr; - auto ret = sqlite3_open_v2(db_name_utf8.data(), &tmp, static_cast(config.flags), config.zVfs); - _db = std::shared_ptr(tmp, [=](sqlite3 *ptr) { - sqlite3_close_v2(ptr); - }); // this will close the connection eventually when no longer needed. - if(ret != SQLITE_OK) - errors::throw_sqlite_error(_db ? sqlite3_extended_errcode(_db.get()) : ret); - sqlite3_extended_result_codes(_db.get(), true); - if(config.encoding != Encoding::UTF8) - *this << R"(PRAGMA encoding = "UTF-16";)"; - } - - database(std::shared_ptr db) : _db(db) - { - } - - database_binder operator<<(const std::string &sql) - { - return database_binder(_db, sql); - } - - database_binder operator<<(const char *sql) - { - return *this << std::string(sql); - } - - database_binder operator<<(const std::u16string &sql) - { - return database_binder(_db, sql); - } - - database_binder operator<<(const char16_t *sql) - { - return *this << std::u16string(sql); - } - - connection_type connection() const - { - return _db; - } - - sqlite3_int64 last_insert_rowid() const - { - return sqlite3_last_insert_rowid(_db.get()); - } - - template void define(const std::string &name, Function &&func) - { - typedef utility::function_traits traits; - - auto funcPtr = new auto(std::forward(func)); - if(int result = sqlite3_create_function_v2( - _db.get(), name.c_str(), traits::arity, SQLITE_UTF8, funcPtr, - sql_function_binder::scalar::type>, nullptr, - nullptr, [](void *ptr) { delete static_cast(ptr); })) - errors::throw_sqlite_error(result); - } - - template - void define(const std::string &name, StepFunction &&step, FinalFunction &&final) - { - typedef utility::function_traits traits; - using ContextType = typename std::remove_reference>::type; - - auto funcPtr = new auto(std::make_pair(std::forward(step), std::forward(final))); - if(int result = sqlite3_create_function_v2( - _db.get(), name.c_str(), traits::arity - 1, SQLITE_UTF8, funcPtr, nullptr, - sql_function_binder::step::type>, - sql_function_binder::final::type>, - [](void *ptr) { delete static_cast(ptr); })) - errors::throw_sqlite_error(result); - } -}; - -template class binder -{ - private: - template - using nth_argument_type = typename utility::function_traits::template argument; - - public: - // `Boundary` needs to be defaulted to `Count` so that the `run` function - // template is not implicitly instantiated on class template instantiation. - // Look up section 14.7.1 _Implicit instantiation_ of the ISO C++14 Standard - // and the [dicussion](https://github.com/aminroosta/sqlite_modern_cpp/issues/8) - // on Github. - - template - static typename std::enable_if<(sizeof...(Values) < Boundary), void>::type run(database_binder &db, - Function &&function, - Values &&...values) - { - typename std::remove_cv< - typename std::remove_reference>::type>::type value{}; - get_col_from_db(db, sizeof...(Values), value); - - run(db, function, std::forward(values)..., std::move(value)); - } - - template - static typename std::enable_if<(sizeof...(Values) == Boundary), void>::type run(database_binder &, - Function &&function, - Values &&...values) - { - function(std::move(values)...); - } -}; - -// int -inline database_binder &operator<<(database_binder &db, const int &val) -{ - int hresult; - if((hresult = sqlite3_bind_int(db._stmt.get(), db._next_index(), val)) != SQLITE_OK) - { - errors::throw_sqlite_error(hresult, db.sql()); - } - return db; -} -inline void store_result_in_db(sqlite3_context *db, const int &val) -{ - sqlite3_result_int(db, val); -} -inline void get_col_from_db(database_binder &db, int inx, int &val) -{ - if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) - { - val = 0; - } - else - { - val = sqlite3_column_int(db._stmt.get(), inx); - } -} -inline void get_val_from_db(sqlite3_value *value, int &val) -{ - if(sqlite3_value_type(value) == SQLITE_NULL) - { - val = 0; - } - else - { - val = sqlite3_value_int(value); - } -} - -// sqlite_int64 -inline database_binder &operator<<(database_binder &db, const sqlite_int64 &val) -{ - int hresult; - if((hresult = sqlite3_bind_int64(db._stmt.get(), db._next_index(), val)) != SQLITE_OK) - { - errors::throw_sqlite_error(hresult, db.sql()); - } - - return db; -} -inline void store_result_in_db(sqlite3_context *db, const sqlite_int64 &val) -{ - sqlite3_result_int64(db, val); -} -inline void get_col_from_db(database_binder &db, int inx, sqlite3_int64 &i) -{ - if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) - { - i = 0; - } - else - { - i = sqlite3_column_int64(db._stmt.get(), inx); - } -} -inline void get_val_from_db(sqlite3_value *value, sqlite3_int64 &i) -{ - if(sqlite3_value_type(value) == SQLITE_NULL) - { - i = 0; - } - else - { - i = sqlite3_value_int64(value); - } -} - -// float -inline database_binder &operator<<(database_binder &db, const float &val) -{ - int hresult; - if((hresult = sqlite3_bind_double(db._stmt.get(), db._next_index(), double(val))) != SQLITE_OK) - { - errors::throw_sqlite_error(hresult, db.sql()); - } - - return db; -} -inline void store_result_in_db(sqlite3_context *db, const float &val) -{ - sqlite3_result_double(db, val); -} -inline void get_col_from_db(database_binder &db, int inx, float &f) -{ - if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) - { - f = 0; - } - else - { - f = float(sqlite3_column_double(db._stmt.get(), inx)); - } -} -inline void get_val_from_db(sqlite3_value *value, float &f) -{ - if(sqlite3_value_type(value) == SQLITE_NULL) - { - f = 0; - } - else - { - f = float(sqlite3_value_double(value)); - } -} - -// double -inline database_binder &operator<<(database_binder &db, const double &val) -{ - int hresult; - if((hresult = sqlite3_bind_double(db._stmt.get(), db._next_index(), val)) != SQLITE_OK) - { - errors::throw_sqlite_error(hresult, db.sql()); - } - - return db; -} -inline void store_result_in_db(sqlite3_context *db, const double &val) -{ - sqlite3_result_double(db, val); -} -inline void get_col_from_db(database_binder &db, int inx, double &d) -{ - if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) - { - d = 0; - } - else - { - d = sqlite3_column_double(db._stmt.get(), inx); - } -} -inline void get_val_from_db(sqlite3_value *value, double &d) -{ - if(sqlite3_value_type(value) == SQLITE_NULL) - { - d = 0; - } - else - { - d = sqlite3_value_double(value); - } -} - -// vector -template inline database_binder &operator<<(database_binder &db, const std::vector &vec) -{ - void const *buf = reinterpret_cast(vec.data()); - int bytes = vec.size() * sizeof(T); - int hresult; - if((hresult = sqlite3_bind_blob(db._stmt.get(), db._next_index(), buf, bytes, SQLITE_TRANSIENT)) != SQLITE_OK) - { - errors::throw_sqlite_error(hresult, db.sql()); - } - return db; -} -template inline void store_result_in_db(sqlite3_context *db, const std::vector &vec) -{ - void const *buf = reinterpret_cast(vec.data()); - int bytes = vec.size() * sizeof(T); - sqlite3_result_blob(db, buf, bytes, SQLITE_TRANSIENT); -} -template inline void get_col_from_db(database_binder &db, int inx, std::vector &vec) -{ - if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) - { - vec.clear(); - } - else - { - int bytes = sqlite3_column_bytes(db._stmt.get(), inx); - T const *buf = reinterpret_cast(sqlite3_column_blob(db._stmt.get(), inx)); - vec = std::vector(buf, buf + bytes / sizeof(T)); - } -} -template inline void get_val_from_db(sqlite3_value *value, std::vector &vec) -{ - if(sqlite3_value_type(value) == SQLITE_NULL) - { - vec.clear(); - } - else - { - int bytes = sqlite3_value_bytes(value); - T const *buf = reinterpret_cast(sqlite3_value_blob(value)); - vec = std::vector(buf, buf + bytes / sizeof(T)); - } -} - -/* for nullptr support */ -inline database_binder &operator<<(database_binder &db, std::nullptr_t) -{ - int hresult; - if((hresult = sqlite3_bind_null(db._stmt.get(), db._next_index())) != SQLITE_OK) - { - errors::throw_sqlite_error(hresult, db.sql()); - } - return db; -} -inline void store_result_in_db(sqlite3_context *db, std::nullptr_t) -{ - sqlite3_result_null(db); -} -/* for nullptr support */ -template inline database_binder &operator<<(database_binder &db, const std::unique_ptr &val) -{ - if(val) - db << *val; - else - db << nullptr; - return db; -} - -/* for unique_ptr support */ -template inline void get_col_from_db(database_binder &db, int inx, std::unique_ptr &_ptr_) -{ - if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) - { - _ptr_ = nullptr; - } - else - { - auto underling_ptr = new T(); - get_col_from_db(db, inx, *underling_ptr); - _ptr_.reset(underling_ptr); - } -} -template inline void get_val_from_db(sqlite3_value *value, std::unique_ptr &_ptr_) -{ - if(sqlite3_value_type(value) == SQLITE_NULL) - { - _ptr_ = nullptr; - } - else - { - auto underling_ptr = new T(); - get_val_from_db(value, *underling_ptr); - _ptr_.reset(underling_ptr); - } -} - -// std::string -inline void get_col_from_db(database_binder &db, int inx, std::string &s) -{ - if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) - { - s = std::string(); - } - else - { - sqlite3_column_bytes(db._stmt.get(), inx); - s = std::string(reinterpret_cast(sqlite3_column_text(db._stmt.get(), inx))); - } -} -inline void get_val_from_db(sqlite3_value *value, std::string &s) -{ - if(sqlite3_value_type(value) == SQLITE_NULL) - { - s = std::string(); - } - else - { - sqlite3_value_bytes(value); - s = std::string(reinterpret_cast(sqlite3_value_text(value))); - } -} - -// Convert char* to string to trigger op<<(..., const std::string ) -template inline database_binder &operator<<(database_binder &db, const char (&STR)[N]) -{ - return db << std::string(STR); -} -template inline database_binder &operator<<(database_binder &db, const char16_t (&STR)[N]) -{ - return db << std::u16string(STR); -} - -inline database_binder &operator<<(database_binder &db, const std::string &txt) -{ - int hresult; - if((hresult = sqlite3_bind_text(db._stmt.get(), db._next_index(), txt.data(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) - { - errors::throw_sqlite_error(hresult, db.sql()); - } - - return db; -} -inline void store_result_in_db(sqlite3_context *db, const std::string &val) -{ - sqlite3_result_text(db, val.data(), -1, SQLITE_TRANSIENT); -} -// std::u16string -inline void get_col_from_db(database_binder &db, int inx, std::u16string &w) -{ - if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) - { - w = std::u16string(); - } - else - { - sqlite3_column_bytes16(db._stmt.get(), inx); - w = std::u16string(reinterpret_cast(sqlite3_column_text16(db._stmt.get(), inx))); - } -} -inline void get_val_from_db(sqlite3_value *value, std::u16string &w) -{ - if(sqlite3_value_type(value) == SQLITE_NULL) - { - w = std::u16string(); - } - else - { - sqlite3_value_bytes16(value); - w = std::u16string(reinterpret_cast(sqlite3_value_text16(value))); - } -} - -inline database_binder &operator<<(database_binder &db, const std::u16string &txt) -{ - int hresult; - if((hresult = sqlite3_bind_text16(db._stmt.get(), db._next_index(), txt.data(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) - { - errors::throw_sqlite_error(hresult, db.sql()); - } - - return db; -} -inline void store_result_in_db(sqlite3_context *db, const std::u16string &val) -{ - sqlite3_result_text16(db, val.data(), -1, SQLITE_TRANSIENT); -} - -// Other integer types -template ::value>::type> -inline database_binder &operator<<(database_binder &db, const Integral &val) -{ - return db << static_cast(val); -} -template ::type>> -inline void store_result_in_db(sqlite3_context *db, const Integral &val) -{ - store_result_in_db(db, static_cast(val)); -} -template ::value>::type> -inline void get_col_from_db(database_binder &db, int inx, Integral &val) -{ - sqlite3_int64 i; - get_col_from_db(db, inx, i); - val = i; -} -template ::value>::type> -inline void get_val_from_db(sqlite3_value *value, Integral &val) -{ - sqlite3_int64 i; - get_val_from_db(value, i); - val = i; -} - -// std::optional support for NULL values -#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT -template inline database_binder &operator<<(database_binder &db, const optional &val) -{ - if(val) - { - return db << std::move(*val); - } - else - { - return db << nullptr; - } -} -template inline void store_result_in_db(sqlite3_context *db, const optional &val) -{ - if(val) - { - store_result_in_db(db, *val); - } - sqlite3_result_null(db); -} - -template inline void get_col_from_db(database_binder &db, int inx, optional &o) -{ - if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) - { -#ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT - o = std::experimental::nullopt; -#else - o.reset(); -#endif - } - else - { - OptionalT v; - get_col_from_db(db, inx, v); - o = std::move(v); - } -} -template inline void get_val_from_db(sqlite3_value *value, optional &o) -{ - if(sqlite3_value_type(value) == SQLITE_NULL) - { -#ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT - o = std::experimental::nullopt; -#else - o.reset(); -#endif - } - else - { - OptionalT v; - get_val_from_db(value, v); - o = std::move(v); - } -} -#endif - -// boost::optional support for NULL values -#ifdef _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT -template -inline database_binder &operator<<(database_binder &db, const boost::optional &val) -{ - if(val) - { - return db << std::move(*val); - } - else - { - return db << nullptr; - } -} -template -inline void store_result_in_db(sqlite3_context *db, const boost::optional &val) -{ - if(val) - { - store_result_in_db(db, *val); - } - sqlite3_result_null(db); -} - -template -inline void get_col_from_db(database_binder &db, int inx, boost::optional &o) -{ - if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) - { - o.reset(); - } - else - { - BoostOptionalT v; - get_col_from_db(db, inx, v); - o = std::move(v); - } -} -template inline void get_val_from_db(sqlite3_value *value, boost::optional &o) -{ - if(sqlite3_value_type(value) == SQLITE_NULL) - { - o.reset(); - } - else - { - BoostOptionalT v; - get_val_from_db(value, v); - o = std::move(v); - } -} -#endif - -#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT -template inline database_binder &operator<<(database_binder &db, const std::variant &val) -{ - std::visit([&](auto &&opt) { db << std::forward(opt); }, val); - return db; -} -template inline void store_result_in_db(sqlite3_context *db, const std::variant &val) -{ - std::visit([&](auto &&opt) { store_result_in_db(db, std::forward(opt)); }, val); -} -template inline void get_col_from_db(database_binder &db, int inx, std::variant &val) -{ - utility::variant_select(sqlite3_column_type(db._stmt.get(), inx))([&](auto v) { - get_col_from_db(db, inx, v); - val = std::move(v); - }); -} -template inline void get_val_from_db(sqlite3_value *value, std::variant &val) -{ - utility::variant_select(sqlite3_value_type(value))([&](auto v) { - get_val_from_db(value, v); - val = std::move(v); - }); -} -#endif - -// Some ppl are lazy so we have a operator for proper prep. statemant handling. -void inline operator++(database_binder &db, int) -{ - db.execute(); -} - -// Convert the rValue binder to a reference and call first op<<, its needed for the call that creates the binder (be -// carefull of recursion here!) -template database_binder &&operator<<(database_binder &&db, const T &val) -{ - db << val; - return std::move(db); -} - -namespace sql_function_binder -{ -template struct AggregateCtxt -{ - T obj; - bool constructed = true; -}; - -template -inline void step(sqlite3_context *db, int count, sqlite3_value **vals) -{ - auto ctxt = - static_cast *>(sqlite3_aggregate_context(db, sizeof(AggregateCtxt))); - if(!ctxt) - return; - try - { - if(!ctxt->constructed) - new(ctxt) AggregateCtxt(); - step(db, count, vals, ctxt->obj); - return; - } - catch(sqlite_exception &e) - { - sqlite3_result_error_code(db, e.get_code()); - sqlite3_result_error(db, e.what(), -1); - } - catch(std::exception &e) - { - sqlite3_result_error(db, e.what(), -1); - } - catch(...) - { - sqlite3_result_error(db, "Unknown error", -1); - } - if(ctxt && ctxt->constructed) - ctxt->~AggregateCtxt(); -} - -template -inline typename std::enable_if<(sizeof...(Values) && sizeof...(Values) < Count), void>::type step(sqlite3_context *db, - int count, - sqlite3_value **vals, - Values &&...values) -{ - typename std::remove_cv::template argument>::type>::type value{}; - get_val_from_db(vals[sizeof...(Values) - 1], value); - - step(db, count, vals, std::forward(values)..., std::move(value)); -} - -template -inline typename std::enable_if<(sizeof...(Values) == Count), void>::type step(sqlite3_context *db, int, - sqlite3_value **, Values &&...values) -{ - static_cast(sqlite3_user_data(db))->first(std::forward(values)...); -} - -template inline void final(sqlite3_context *db) -{ - auto ctxt = - static_cast *>(sqlite3_aggregate_context(db, sizeof(AggregateCtxt))); - try - { - if(!ctxt) - return; - if(!ctxt->constructed) - new(ctxt) AggregateCtxt(); - store_result_in_db(db, static_cast(sqlite3_user_data(db))->second(ctxt->obj)); - } - catch(sqlite_exception &e) - { - sqlite3_result_error_code(db, e.get_code()); - sqlite3_result_error(db, e.what(), -1); - } - catch(std::exception &e) - { - sqlite3_result_error(db, e.what(), -1); - } - catch(...) - { - sqlite3_result_error(db, "Unknown error", -1); - } - if(ctxt && ctxt->constructed) - ctxt->~AggregateCtxt(); -} - -template -inline typename std::enable_if<(sizeof...(Values) < Count), void>::type scalar(sqlite3_context *db, int count, - sqlite3_value **vals, Values &&...values) -{ - typename std::remove_cv::template argument>::type>::type value{}; - get_val_from_db(vals[sizeof...(Values)], value); - - scalar(db, count, vals, std::forward(values)..., std::move(value)); -} - -template -inline typename std::enable_if<(sizeof...(Values) == Count), void>::type scalar(sqlite3_context *db, int, - sqlite3_value **, Values &&...values) -{ - try - { - store_result_in_db(db, (*static_cast(sqlite3_user_data(db)))(std::forward(values)...)); - } - catch(sqlite_exception &e) - { - sqlite3_result_error_code(db, e.get_code()); - sqlite3_result_error(db, e.what(), -1); - } - catch(std::exception &e) - { - sqlite3_result_error(db, e.what(), -1); - } - catch(...) - { - sqlite3_result_error(db, "Unknown error", -1); - } -} -} // namespace sql_function_binder -} // namespace sqlite diff --git a/database/hdr/sqlite_modern_cpp/errors.h b/database/hdr/sqlite_modern_cpp/errors.h deleted file mode 100644 index a472bca..0000000 --- a/database/hdr/sqlite_modern_cpp/errors.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace sqlite -{ - -class sqlite_exception : public std::runtime_error -{ - public: - sqlite_exception(const char *msg, std::string sql, int code = -1) : runtime_error(msg), code(code), sql(sql) - { - } - sqlite_exception(int code, std::string sql) : runtime_error(sqlite3_errstr(code)), code(code), sql(sql) - { - } - int get_code() const - { - return code & 0xFF; - } - int get_extended_code() const - { - return code; - } - std::string get_sql() const - { - return sql; - } - - private: - int code; - std::string sql; -}; - -namespace errors -{ -// One more or less trivial derived error class for each SQLITE error. -// Note the following are not errors so have no classes: -// SQLITE_OK, SQLITE_NOTICE, SQLITE_WARNING, SQLITE_ROW, SQLITE_DONE -// -// Note these names are exact matches to the names of the SQLITE error codes. -#define SQLITE_MODERN_CPP_ERROR_CODE(NAME, name, derived) \ - class name : public sqlite_exception \ - { \ - using sqlite_exception::sqlite_exception; \ - }; \ - derived -#define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE, SUB, base, sub) \ - class base##_##sub : public base \ - { \ - using base::base; \ - }; -#include "lists/error_codes.h" -#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED -#undef SQLITE_MODERN_CPP_ERROR_CODE - -// Some additional errors are here for the C++ interface -class more_rows : public sqlite_exception -{ - using sqlite_exception::sqlite_exception; -}; -class no_rows : public sqlite_exception -{ - using sqlite_exception::sqlite_exception; -}; -class more_statements : public sqlite_exception -{ - using sqlite_exception::sqlite_exception; -}; // Prepared statements can only contain one statement -class invalid_utf16 : public sqlite_exception -{ - using sqlite_exception::sqlite_exception; -}; - -static void throw_sqlite_error(const int &error_code, const std::string &sql = "") -{ - switch(error_code & 0xFF) - { -#define SQLITE_MODERN_CPP_ERROR_CODE(NAME, name, derived) \ - case SQLITE_##NAME: \ - switch(error_code) \ - { \ - derived default : throw name(error_code, sql); \ - } -#define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE, SUB, base, sub) \ - case SQLITE_##BASE##_##SUB: \ - throw base##_##sub(error_code, sql); -#include "lists/error_codes.h" -#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED -#undef SQLITE_MODERN_CPP_ERROR_CODE - default: - throw sqlite_exception(error_code, sql); - } -} -} // namespace errors -namespace exceptions = errors; -} // namespace sqlite diff --git a/database/hdr/sqlite_modern_cpp/lists/error_codes.h b/database/hdr/sqlite_modern_cpp/lists/error_codes.h deleted file mode 100644 index 3ac8e37..0000000 --- a/database/hdr/sqlite_modern_cpp/lists/error_codes.h +++ /dev/null @@ -1,90 +0,0 @@ -#if SQLITE_VERSION_NUMBER < 3010000 -#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27 << 8)) -#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28 << 8)) -#define SQLITE_AUTH_USER (SQLITE_AUTH | (1 << 8)) -#endif -SQLITE_MODERN_CPP_ERROR_CODE(ERROR, error, ) -SQLITE_MODERN_CPP_ERROR_CODE(INTERNAL, internal, ) -SQLITE_MODERN_CPP_ERROR_CODE(PERM, perm, ) -SQLITE_MODERN_CPP_ERROR_CODE(ABORT, abort, SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(ABORT, ROLLBACK, abort, rollback)) -SQLITE_MODERN_CPP_ERROR_CODE(BUSY, busy, - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BUSY, RECOVERY, busy, recovery) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BUSY, SNAPSHOT, busy, snapshot)) -SQLITE_MODERN_CPP_ERROR_CODE(LOCKED, locked, - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(LOCKED, SHAREDCACHE, locked, sharedcache)) -SQLITE_MODERN_CPP_ERROR_CODE(NOMEM, nomem, ) -SQLITE_MODERN_CPP_ERROR_CODE(READONLY, readonly, ) -SQLITE_MODERN_CPP_ERROR_CODE(INTERRUPT, interrupt, ) -SQLITE_MODERN_CPP_ERROR_CODE( - IOERR, ioerr, - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, READ, ioerr, read) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED( - IOERR, SHORT_READ, ioerr, - short_read) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, WRITE, ioerr, - write) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, FSYNC, - ioerr, fsync) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, DIR_FSYNC, ioerr, dir_fsync) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED( - IOERR, TRUNCATE, ioerr, - truncate) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, FSTAT, ioerr, - fstat) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, UNLOCK, - ioerr, unlock) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, RDLOCK, ioerr, rdlock) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED( - IOERR, DELETE, ioerr, delete) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, BLOCKED, ioerr, blocked) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, NOMEM, ioerr, nomem) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED( - IOERR, ACCESS, ioerr, access) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, CHECKRESERVEDLOCK, ioerr, - checkreservedlock) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, LOCK, ioerr, lock) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, CLOSE, ioerr, close) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, DIR_CLOSE, ioerr, dir_close) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, SHMOPEN, ioerr, shmopen) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, SHMSIZE, ioerr, shmsize) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, SHMLOCK, ioerr, shmlock) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, SHMMAP, ioerr, shmmap) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, SEEK, ioerr, seek) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, DELETE_NOENT, ioerr, - delete_noent) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, MMAP, ioerr, mmap) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, GETTEMPPATH, - ioerr, gettemppath) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, CONVPATH, - ioerr, convpath) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR, VNODE, - ioerr, vnode) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED( - IOERR, AUTH, ioerr, auth)) -SQLITE_MODERN_CPP_ERROR_CODE(CORRUPT, corrupt, SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CORRUPT, VTAB, corrupt, vtab)) -SQLITE_MODERN_CPP_ERROR_CODE(NOTFOUND, notfound, ) -SQLITE_MODERN_CPP_ERROR_CODE(FULL, full, ) -SQLITE_MODERN_CPP_ERROR_CODE(CANTOPEN, cantopen, - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CANTOPEN, NOTEMPDIR, cantopen, notempdir) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CANTOPEN, ISDIR, cantopen, isdir) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CANTOPEN, FULLPATH, cantopen, fullpath) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CANTOPEN, CONVPATH, cantopen, convpath)) -SQLITE_MODERN_CPP_ERROR_CODE(PROTOCOL, protocol, ) -SQLITE_MODERN_CPP_ERROR_CODE(EMPTY, empty, ) -SQLITE_MODERN_CPP_ERROR_CODE(SCHEMA, schema, ) -SQLITE_MODERN_CPP_ERROR_CODE(TOOBIG, toobig, ) -SQLITE_MODERN_CPP_ERROR_CODE( - CONSTRAINT, constraint, - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT, CHECK, constraint, check) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT, COMMITHOOK, constraint, commithook) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT, FOREIGNKEY, constraint, foreignkey) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT, FUNCTION, constraint, function) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT, NOTNULL, constraint, notnull) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT, PRIMARYKEY, constraint, primarykey) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT, TRIGGER, constraint, trigger) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT, UNIQUE, constraint, unique) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT, VTAB, constraint, vtab) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT, ROWID, constraint, rowid)) -SQLITE_MODERN_CPP_ERROR_CODE(MISMATCH, mismatch, ) -SQLITE_MODERN_CPP_ERROR_CODE(MISUSE, misuse, ) -SQLITE_MODERN_CPP_ERROR_CODE(NOLFS, nolfs, ) -SQLITE_MODERN_CPP_ERROR_CODE(AUTH, auth, ) -SQLITE_MODERN_CPP_ERROR_CODE(FORMAT, format, ) -SQLITE_MODERN_CPP_ERROR_CODE(RANGE, range, ) -SQLITE_MODERN_CPP_ERROR_CODE(NOTADB, notadb, ) -SQLITE_MODERN_CPP_ERROR_CODE(NOTICE, notice, - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(NOTICE, RECOVER_WAL, notice, recover_wal) - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(NOTICE, RECOVER_ROLLBACK, notice, - recover_rollback)) -SQLITE_MODERN_CPP_ERROR_CODE(WARNING, warning, - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(WARNING, AUTOINDEX, warning, autoindex)) diff --git a/database/hdr/sqlite_modern_cpp/log.h b/database/hdr/sqlite_modern_cpp/log.h deleted file mode 100644 index c636076..0000000 --- a/database/hdr/sqlite_modern_cpp/log.h +++ /dev/null @@ -1,124 +0,0 @@ -#include "errors.h" - -#include - -#include -#include -#include - -namespace sqlite -{ -namespace detail -{ -template using void_t = void; -template struct is_callable : std::false_type -{ -}; -template -struct is_callable()(std::declval()...))>> - : std::true_type -{ -}; -template class FunctorOverload : public Functor, public FunctorOverload -{ - public: - template - FunctorOverload(Functor1 &&functor, Remaining &&...remaining) - : Functor(std::forward(functor)), FunctorOverload(std::forward(remaining)...) - { - } - using Functor::operator(); - using FunctorOverload::operator(); -}; -template class FunctorOverload : public Functor -{ - public: - template FunctorOverload(Functor1 &&functor) : Functor(std::forward(functor)) - { - } - using Functor::operator(); -}; -template class WrapIntoFunctor : public Functor -{ - public: - template WrapIntoFunctor(Functor1 &&functor) : Functor(std::forward(functor)) - { - } - using Functor::operator(); -}; -template class WrapIntoFunctor -{ - ReturnType (*ptr)(Arguments...); - - public: - WrapIntoFunctor(ReturnType (*ptr)(Arguments...)) : ptr(ptr) - { - } - ReturnType operator()(Arguments... arguments) - { - return (*ptr)(std::forward(arguments)...); - } -}; -inline void store_error_log_data_pointer(std::shared_ptr ptr) -{ - static std::shared_ptr stored; - stored = std::move(ptr); -} -template std::shared_ptr::type> make_shared_inferred(T &&t) -{ - return std::make_shared::type>(std::forward(t)); -} -} // namespace detail -template -typename std::enable_if::value>::type error_log( - Handler &&handler); -template -typename std::enable_if::value>::type error_log( - Handler &&handler); -template typename std::enable_if= 2>::type error_log(Handler &&...handler) -{ - return error_log(detail::FunctorOverload::type>...>( - std::forward(handler)...)); -} -template -typename std::enable_if::value>::type error_log( - Handler &&handler) -{ - return error_log(std::forward(handler), [](const sqlite_exception &) {}); -} -template -typename std::enable_if::value>::type error_log( - Handler &&handler) -{ - auto ptr = detail::make_shared_inferred( - [handler = std::forward(handler)](int error_code, const char *errstr) mutable { - switch(error_code & 0xFF) - { -#define SQLITE_MODERN_CPP_ERROR_CODE(NAME, name, derived) \ - case SQLITE_##NAME: \ - switch(error_code) \ - { \ - derived default : handler(errors::name(errstr, "", error_code)); \ - }; \ - break; -#define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE, SUB, base, sub) \ - case SQLITE_##BASE##_##SUB: \ - handler(errors::base##_##sub(errstr, "", error_code)); \ - break; -#include "lists/error_codes.h" -#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED -#undef SQLITE_MODERN_CPP_ERROR_CODE - default: - handler(sqlite_exception(errstr, "", error_code)); - } - }); - - sqlite3_config( - SQLITE_CONFIG_LOG, - (void (*)(void *, int, const char *))[](void *functor, int error_code, const char *errstr) { - (*static_cast(functor))(error_code, errstr); - }, - ptr.get()); - detail::store_error_log_data_pointer(std::move(ptr)); -} -} // namespace sqlite diff --git a/database/hdr/sqlite_modern_cpp/sqlcipher.h b/database/hdr/sqlite_modern_cpp/sqlcipher.h deleted file mode 100644 index f6eeb07..0000000 --- a/database/hdr/sqlite_modern_cpp/sqlcipher.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#ifndef SQLITE_HAS_CODEC -#define SQLITE_HAS_CODEC -#endif - -#include "../sqlite_modern_cpp.h" - -namespace sqlite -{ -struct sqlcipher_config : public sqlite_config -{ - std::string key; -}; - -class sqlcipher_database : public database -{ - public: - sqlcipher_database(std::string db, const sqlcipher_config &config) : database(db, config) - { - set_key(config.key); - } - - sqlcipher_database(std::u16string db, const sqlcipher_config &config) : database(db, config) - { - set_key(config.key); - } - - void set_key(const std::string &key) - { - if(auto ret = sqlite3_key(_db.get(), key.data(), key.size())) - errors::throw_sqlite_error(ret); - } - - void set_key(const std::string &key, const std::string &db_name) - { - if(auto ret = sqlite3_key_v2(_db.get(), db_name.c_str(), key.data(), key.size())) - errors::throw_sqlite_error(ret); - } - - void rekey(const std::string &new_key) - { - if(auto ret = sqlite3_rekey(_db.get(), new_key.data(), new_key.size())) - errors::throw_sqlite_error(ret); - } - - void rekey(const std::string &new_key, const std::string &db_name) - { - if(auto ret = sqlite3_rekey_v2(_db.get(), db_name.c_str(), new_key.data(), new_key.size())) - errors::throw_sqlite_error(ret); - } -}; -} // namespace sqlite diff --git a/database/hdr/sqlite_modern_cpp/utility/function_traits.h b/database/hdr/sqlite_modern_cpp/utility/function_traits.h deleted file mode 100644 index 770e9bb..0000000 --- a/database/hdr/sqlite_modern_cpp/utility/function_traits.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include -#include - -namespace sqlite -{ -namespace utility -{ - -template struct function_traits; - -template -struct function_traits : public function_traits::type::operator())> -{ -}; - -template -struct function_traits : function_traits -{ -}; - -/* support the non-const operator () - * this will work with user defined functors */ -template -struct function_traits : function_traits -{ -}; - -template struct function_traits -{ - typedef ReturnType result_type; - - template using argument = typename std::tuple_element>::type; - - static const std::size_t arity = sizeof...(Arguments); -}; - -} // namespace utility -} // namespace sqlite diff --git a/database/hdr/sqlite_modern_cpp/utility/uncaught_exceptions.h b/database/hdr/sqlite_modern_cpp/utility/uncaught_exceptions.h deleted file mode 100644 index d980cfa..0000000 --- a/database/hdr/sqlite_modern_cpp/utility/uncaught_exceptions.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace sqlite -{ -namespace utility -{ -#ifdef __cpp_lib_uncaught_exceptions -class UncaughtExceptionDetector -{ - public: - operator bool() - { - return count != std::uncaught_exceptions(); - } - - private: - int count = std::uncaught_exceptions(); -}; -#else -class UncaughtExceptionDetector -{ - public: - operator bool() - { - return std::uncaught_exception(); - } -}; -#endif -} // namespace utility -} // namespace sqlite diff --git a/database/hdr/sqlite_modern_cpp/utility/utf16_utf8.h b/database/hdr/sqlite_modern_cpp/utility/utf16_utf8.h deleted file mode 100644 index d12674c..0000000 --- a/database/hdr/sqlite_modern_cpp/utility/utf16_utf8.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "../errors.h" - -namespace sqlite -{ -namespace utility -{ -inline std::string utf16_to_utf8(const std::u16string &input) -{ - struct : std::codecvt - { - } codecvt; - std::mbstate_t state{}; - std::string result((std::max)(input.size() * 3 / 2, std::size_t(4)), '\0'); - const char16_t *remaining_input = input.data(); - std::size_t produced_output = 0; - while(true) - { - char *used_output; - switch(codecvt.out(state, remaining_input, &input[input.size()], remaining_input, &result[produced_output], - &result[result.size() - 1] + 1, used_output)) - { - case std::codecvt_base::ok: - result.resize(used_output - result.data()); - return result; - case std::codecvt_base::noconv: - // This should be unreachable - case std::codecvt_base::error: - throw errors::invalid_utf16("Invalid UTF-16 input", ""); - case std::codecvt_base::partial: - if(used_output == result.data() + produced_output) - throw errors::invalid_utf16("Unexpected end of input", ""); - produced_output = used_output - result.data(); - result.resize(result.size() + - (std::max)((&input[input.size()] - remaining_input) * 3 / 2, std::ptrdiff_t(4))); - } - } -} -} // namespace utility -} // namespace sqlite diff --git a/database/hdr/sqlite_modern_cpp/utility/variant.h b/database/hdr/sqlite_modern_cpp/utility/variant.h deleted file mode 100644 index fec6c8f..0000000 --- a/database/hdr/sqlite_modern_cpp/utility/variant.h +++ /dev/null @@ -1,230 +0,0 @@ -#pragma once - -#include "../errors.h" -#include -#include -#include - -namespace sqlite::utility -{ -template struct VariantFirstNullable -{ - using type = void; -}; -template struct VariantFirstNullable -{ - using type = typename VariantFirstNullable::type; -}; -#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT -template struct VariantFirstNullable, Options...> -{ - using type = std::optional; -}; -#endif -template struct VariantFirstNullable, Options...> -{ - using type = std::unique_ptr; -}; -template struct VariantFirstNullable -{ - using type = std::nullptr_t; -}; -template inline void variant_select_null(Callback &&callback) -{ - if constexpr(std::is_same_v::type, void>) - { - throw errors::mismatch("NULL is unsupported by this variant.", "", SQLITE_MISMATCH); - } - else - { - std::forward(callback)(typename VariantFirstNullable::type()); - } -} - -template struct VariantFirstIntegerable -{ - using type = void; -}; -template struct VariantFirstIntegerable -{ - using type = typename VariantFirstIntegerable::type; -}; -#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT -template struct VariantFirstIntegerable, Options...> -{ - using type = std::conditional_t::type, T>, - std::optional, typename VariantFirstIntegerable::type>; -}; -#endif -template -struct VariantFirstIntegerable< - std::enable_if_t::type, T>>, std::unique_ptr, - Options...> -{ - using type = std::conditional_t::type, T>, - std::unique_ptr, typename VariantFirstIntegerable::type>; -}; -template struct VariantFirstIntegerable -{ - using type = int; -}; -template struct VariantFirstIntegerable -{ - using type = sqlite_int64; -}; -template inline auto variant_select_integer(Callback &&callback) -{ - if constexpr(std::is_same_v::type, void>) - { - throw errors::mismatch("Integer is unsupported by this variant.", "", SQLITE_MISMATCH); - } - else - { - std::forward(callback)(typename VariantFirstIntegerable::type()); - } -} - -template struct VariantFirstFloatable -{ - using type = void; -}; -template struct VariantFirstFloatable -{ - using type = typename VariantFirstFloatable::type; -}; -#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT -template struct VariantFirstFloatable, Options...> -{ - using type = std::conditional_t::type, T>, - std::optional, typename VariantFirstFloatable::type>; -}; -#endif -template struct VariantFirstFloatable, Options...> -{ - using type = std::conditional_t::type, T>, - std::unique_ptr, typename VariantFirstFloatable::type>; -}; -template struct VariantFirstFloatable -{ - using type = float; -}; -template struct VariantFirstFloatable -{ - using type = double; -}; -template inline auto variant_select_float(Callback &&callback) -{ - if constexpr(std::is_same_v::type, void>) - { - throw errors::mismatch("Real is unsupported by this variant.", "", SQLITE_MISMATCH); - } - else - { - std::forward(callback)(typename VariantFirstFloatable::type()); - } -} - -template struct VariantFirstTextable -{ - using type = void; -}; -template struct VariantFirstTextable -{ - using type = typename VariantFirstTextable::type; -}; -#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT -template struct VariantFirstTextable, Options...> -{ - using type = std::conditional_t::type, T>, - std::optional, typename VariantFirstTextable::type>; -}; -#endif -template struct VariantFirstTextable, Options...> -{ - using type = std::conditional_t::type, T>, - std::unique_ptr, typename VariantFirstTextable::type>; -}; -template struct VariantFirstTextable -{ - using type = std::string; -}; -template struct VariantFirstTextable -{ - using type = std::u16string; -}; -template inline void variant_select_text(Callback &&callback) -{ - if constexpr(std::is_same_v::type, void>) - { - throw errors::mismatch("Text is unsupported by this variant.", "", SQLITE_MISMATCH); - } - else - { - std::forward(callback)(typename VariantFirstTextable::type()); - } -} - -template struct VariantFirstBlobable -{ - using type = void; -}; -template struct VariantFirstBlobable -{ - using type = typename VariantFirstBlobable::type; -}; -#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT -template struct VariantFirstBlobable, Options...> -{ - using type = std::conditional_t::type, T>, - std::optional, typename VariantFirstBlobable::type>; -}; -#endif -template struct VariantFirstBlobable, Options...> -{ - using type = std::conditional_t::type, T>, - std::unique_ptr, typename VariantFirstBlobable::type>; -}; -template -struct VariantFirstBlobable>, std::vector, Options...> -{ - using type = std::vector; -}; -template inline auto variant_select_blob(Callback &&callback) -{ - if constexpr(std::is_same_v::type, void>) - { - throw errors::mismatch("Blob is unsupported by this variant.", "", SQLITE_MISMATCH); - } - else - { - std::forward(callback)(typename VariantFirstBlobable::type()); - } -} - -template inline auto variant_select(int type) -{ - return [type](auto &&callback) { - using Callback = decltype(callback); - switch(type) - { - case SQLITE_NULL: - variant_select_null(std::forward(callback)); - break; - case SQLITE_INTEGER: - variant_select_integer(std::forward(callback)); - break; - case SQLITE_FLOAT: - variant_select_float(std::forward(callback)); - break; - case SQLITE_TEXT: - variant_select_text(std::forward(callback)); - break; - case SQLITE_BLOB: - variant_select_blob(std::forward(callback)); - break; - default:; - /* assert(false); */ - } - }; -} -} // namespace sqlite::utility diff --git a/gateway/httpgateway.h b/gateway/httpgateway.h index af7efd2..b96b82b 100644 --- a/gateway/httpgateway.h +++ b/gateway/httpgateway.h @@ -1,6 +1,6 @@ #ifndef HTTPGATEWAY_H #define HTTPGATEWAY_H -#include "httplib.h" +#include #include "gatewayinterface.h" #include "../requestworker.h" #include "../request.h" diff --git a/gateway/httplib.h b/gateway/httplib.h deleted file mode 100644 index f90dc90..0000000 --- a/gateway/httplib.h +++ /dev/null @@ -1,4431 +0,0 @@ -// -// httplib.h -// -// Copyright (c) 2019 Yuji Hirose. All rights reserved. -// MIT License -// - -#ifndef CPPHTTPLIB_HTTPLIB_H -#define CPPHTTPLIB_HTTPLIB_H - -/* - * Configuration - */ -#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 -#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0 -#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 -#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5 -#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0 -#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 -#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20 -#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits::max)() -#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u) -#define CPPHTTPLIB_THREAD_POOL_COUNT 8 - -#ifdef _WIN32 -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS -#endif //_CRT_SECURE_NO_WARNINGS - -#ifndef _CRT_NONSTDC_NO_DEPRECATE -#define _CRT_NONSTDC_NO_DEPRECATE -#endif //_CRT_NONSTDC_NO_DEPRECATE - -#if defined(_MSC_VER) -#ifdef _WIN64 -typedef __int64 ssize_t; -#else -typedef int ssize_t; -#endif - -#if _MSC_VER < 1900 -#define snprintf _snprintf_s -#endif -#endif // _MSC_VER - -#ifndef S_ISREG -#define S_ISREG(m) (((m)&S_IFREG) == S_IFREG) -#endif // S_ISREG - -#ifndef S_ISDIR -#define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR) -#endif // S_ISDIR - -#ifndef NOMINMAX -#define NOMINMAX -#endif // NOMINMAX - -#include -#include -#include - -#ifndef WSA_FLAG_NO_HANDLE_INHERIT -#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 -#endif - -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#ifndef strcasecmp -#define strcasecmp _stricmp -#endif // strcasecmp - -typedef SOCKET socket_t; -#ifndef CPPHTTPLIB_USE_SELECT -#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout) -#endif - -#else // not _WIN32 - -#include -#include -#include -#include -#ifndef CPPHTTPLIB_USE_SELECT -#include -#endif -#include -#include -#include -#include -#include - -typedef int socket_t; -#define INVALID_SOCKET (-1) -#endif //_WIN32 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -#include -#include -#include - -// #if OPENSSL_VERSION_NUMBER < 0x1010100fL -// #error Sorry, OpenSSL versions prior to 1.1.1 are not supported -// #endif - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include -inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) -{ - return M_ASN1_STRING_data(asn1); -} -#endif -#endif - -#ifdef CPPHTTPLIB_ZLIB_SUPPORT -#include -#endif - -namespace httplib -{ - -namespace detail -{ - -struct ci -{ - bool operator()(const std::string &s1, const std::string &s2) const - { - return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), - [](char c1, char c2) { return ::tolower(c1) < ::tolower(c2); }); - } -}; - -} // namespace detail - -enum class HttpVersion -{ - v1_0 = 0, - v1_1 -}; - -typedef std::multimap Headers; - -typedef std::multimap Params; -typedef std::smatch Match; - -typedef std::function DataSink; - -typedef std::function Done; - -typedef std::function ContentProvider; - -typedef std::function - ContentReceiver; - -typedef std::function Progress; - -struct Response; -typedef std::function ResponseHandler; - -struct MultipartFile -{ - std::string filename; - std::string content_type; - size_t offset = 0; - size_t length = 0; -}; -typedef std::multimap MultipartFiles; - -struct MultipartFormData -{ - std::string name; - std::string content; - std::string filename; - std::string content_type; -}; -typedef std::vector MultipartFormDataItems; - -typedef std::pair Range; -typedef std::vector Ranges; - -struct Request -{ - std::string method; - std::string path; - Headers headers; - std::string body; - - // for server - std::string version; - std::string target; - Params params; - MultipartFiles files; - Ranges ranges; - Match matches; - - // for client - size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT; - ResponseHandler response_handler; - ContentReceiver content_receiver; - Progress progress; - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - const SSL *ssl; -#endif - - bool has_header(const char *key) const; - std::string get_header_value(const char *key, size_t id = 0) const; - size_t get_header_value_count(const char *key) const; - void set_header(const char *key, const char *val); - void set_header(const char *key, const std::string &val); - - bool has_param(const char *key) const; - std::string get_param_value(const char *key, size_t id = 0) const; - size_t get_param_value_count(const char *key) const; - - bool has_file(const char *key) const; - MultipartFile get_file_value(const char *key) const; -}; - -struct Response -{ - std::string version; - int status; - Headers headers; - std::string body; - - bool has_header(const char *key) const; - std::string get_header_value(const char *key, size_t id = 0) const; - size_t get_header_value_count(const char *key) const; - void set_header(const char *key, const char *val); - void set_header(const char *key, const std::string &val); - - void set_redirect(const char *uri); - void set_content(const char *s, size_t n, const char *content_type); - void set_content(const std::string &s, const char *content_type); - - void set_content_provider( - size_t length, std::function provider, - std::function resource_releaser = [] {}); - - void set_chunked_content_provider( - std::function provider, - std::function resource_releaser = [] {}); - - Response() : status(-1), content_provider_resource_length(0) - { - } - - ~Response() - { - if(content_provider_resource_releaser) - { - content_provider_resource_releaser(); - } - } - - size_t content_provider_resource_length; - ContentProvider content_provider; - std::function content_provider_resource_releaser; -}; - -class Stream -{ - public: - virtual ~Stream() - { - } - virtual int read(char *ptr, size_t size) = 0; - virtual int write(const char *ptr, size_t size1) = 0; - virtual int write(const char *ptr) = 0; - virtual int write(const std::string &s) = 0; - virtual std::string get_remote_addr() const = 0; - - template int write_format(const char *fmt, const Args &...args); -}; - -class SocketStream : public Stream -{ - public: - SocketStream(socket_t sock); - virtual ~SocketStream(); - - virtual int read(char *ptr, size_t size); - virtual int write(const char *ptr, size_t size); - virtual int write(const char *ptr); - virtual int write(const std::string &s); - virtual std::string get_remote_addr() const; - - private: - socket_t sock_; -}; - -class BufferStream : public Stream -{ - public: - BufferStream() - { - } - virtual ~BufferStream() - { - } - - virtual int read(char *ptr, size_t size); - virtual int write(const char *ptr, size_t size); - virtual int write(const char *ptr); - virtual int write(const std::string &s); - virtual std::string get_remote_addr() const; - - const std::string &get_buffer() const; - - private: - std::string buffer; -}; - -class TaskQueue -{ - public: - TaskQueue() - { - } - virtual ~TaskQueue() - { - } - virtual void enqueue(std::function fn) = 0; - virtual void shutdown() = 0; -}; - -#if CPPHTTPLIB_THREAD_POOL_COUNT > 0 -class ThreadPool : public TaskQueue -{ - public: - ThreadPool(size_t n) : shutdown_(false) - { - while(n) - { - auto t = std::make_shared(worker(*this)); - threads_.push_back(t); - n--; - } - } - - ThreadPool(const ThreadPool &) = delete; - virtual ~ThreadPool() - { - } - - virtual void enqueue(std::function fn) override - { - std::unique_lock lock(mutex_); - jobs_.push_back(fn); - cond_.notify_one(); - } - - virtual void shutdown() override - { - // Stop all worker threads... - { - std::unique_lock lock(mutex_); - shutdown_ = true; - } - - cond_.notify_all(); - - // Join... - for(auto t : threads_) - { - t->join(); - } - } - - private: - struct worker - { - worker(ThreadPool &pool) : pool_(pool) - { - } - - void operator()() - { - for(;;) - { - std::function fn; - { - std::unique_lock lock(pool_.mutex_); - - pool_.cond_.wait(lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; }); - - if(pool_.shutdown_ && pool_.jobs_.empty()) - { - break; - } - - fn = pool_.jobs_.front(); - pool_.jobs_.pop_front(); - } - - assert(true == static_cast(fn)); - fn(); - } - } - - ThreadPool &pool_; - }; - friend struct worker; - - std::vector> threads_; - std::list> jobs_; - - bool shutdown_; - - std::condition_variable cond_; - std::mutex mutex_; -}; -#else -class Threads : public TaskQueue -{ - public: - Threads() : running_threads_(0) - { - } - virtual ~Threads() - { - } - - virtual void enqueue(std::function fn) override - { - std::thread([=]() { - { - std::lock_guard guard(running_threads_mutex_); - running_threads_++; - } - - fn(); - - { - std::lock_guard guard(running_threads_mutex_); - running_threads_--; - } - }).detach(); - } - - virtual void shutdown() override - { - for(;;) - { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - std::lock_guard guard(running_threads_mutex_); - if(!running_threads_) - { - break; - } - } - } - - private: - std::mutex running_threads_mutex_; - int running_threads_; -}; -#endif - -class Server -{ - public: - typedef std::function Handler; - typedef std::function Logger; - - Server(); - - virtual ~Server(); - - virtual bool is_valid() const; - - Server &Get(const char *pattern, Handler handler); - Server &Post(const char *pattern, Handler handler); - - Server &Put(const char *pattern, Handler handler); - Server &Patch(const char *pattern, Handler handler); - Server &Delete(const char *pattern, Handler handler); - Server &Options(const char *pattern, Handler handler); - - bool set_base_dir(const char *path); - void set_file_request_handler(Handler handler); - - void set_error_handler(Handler handler); - void set_logger(Logger logger); - - void set_keep_alive_max_count(size_t count); - void set_payload_max_length(size_t length); - - int bind_to_any_port(const char *host, int socket_flags = 0); - bool listen_after_bind(); - - bool listen(const char *host, int port, int socket_flags = 0); - - bool is_running() const; - void stop(); - - std::function new_task_queue; - - protected: - bool process_request(Stream &strm, bool last_connection, bool &connection_close, - std::function setup_request); - - size_t keep_alive_max_count_; - size_t payload_max_length_; - - private: - typedef std::vector> Handlers; - - socket_t create_server_socket(const char *host, int port, int socket_flags) const; - int bind_internal(const char *host, int port, int socket_flags); - bool listen_internal(); - - bool routing(Request &req, Response &res); - bool handle_file_request(Request &req, Response &res); - bool dispatch_request(Request &req, Response &res, Handlers &handlers); - - bool parse_request_line(const char *s, Request &req); - bool write_response(Stream &strm, bool last_connection, const Request &req, Response &res); - bool write_content_with_provider(Stream &strm, const Request &req, Response &res, const std::string &boundary, - const std::string &content_type); - - virtual bool process_and_close_socket(socket_t sock); - - std::atomic is_running_; - std::atomic svr_sock_; - std::string base_dir_; - Handler file_request_handler_; - Handlers get_handlers_; - Handlers post_handlers_; - Handlers put_handlers_; - Handlers patch_handlers_; - Handlers delete_handlers_; - Handlers options_handlers_; - Handler error_handler_; - Logger logger_; -}; - -class Client -{ - public: - Client(const char *host, int port = 80, time_t timeout_sec = 300); - - virtual ~Client(); - - virtual bool is_valid() const; - - std::shared_ptr Get(const char *path); - - std::shared_ptr Get(const char *path, const Headers &headers); - - std::shared_ptr Get(const char *path, Progress progress); - - std::shared_ptr Get(const char *path, const Headers &headers, Progress progress); - - std::shared_ptr Get(const char *path, ContentReceiver content_receiver); - - std::shared_ptr Get(const char *path, const Headers &headers, ContentReceiver content_receiver); - - std::shared_ptr Get(const char *path, ContentReceiver content_receiver, Progress progress); - - std::shared_ptr Get(const char *path, const Headers &headers, ContentReceiver content_receiver, - Progress progress); - - std::shared_ptr Get(const char *path, const Headers &headers, ResponseHandler response_handler, - ContentReceiver content_receiver); - - std::shared_ptr Get(const char *path, const Headers &headers, ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress); - - std::shared_ptr Head(const char *path); - - std::shared_ptr Head(const char *path, const Headers &headers); - - std::shared_ptr Post(const char *path, const std::string &body, const char *content_type); - - std::shared_ptr Post(const char *path, const Headers &headers, const std::string &body, - const char *content_type); - - std::shared_ptr Post(const char *path, const Params ¶ms); - - std::shared_ptr Post(const char *path, const Headers &headers, const Params ¶ms); - - std::shared_ptr Post(const char *path, const MultipartFormDataItems &items); - - std::shared_ptr Post(const char *path, const Headers &headers, const MultipartFormDataItems &items); - - std::shared_ptr Put(const char *path, const std::string &body, const char *content_type); - - std::shared_ptr Put(const char *path, const Headers &headers, const std::string &body, - const char *content_type); - - std::shared_ptr Patch(const char *path, const std::string &body, const char *content_type); - - std::shared_ptr Patch(const char *path, const Headers &headers, const std::string &body, - const char *content_type); - - std::shared_ptr Delete(const char *path); - - std::shared_ptr Delete(const char *path, const std::string &body, const char *content_type); - - std::shared_ptr Delete(const char *path, const Headers &headers); - - std::shared_ptr Delete(const char *path, const Headers &headers, const std::string &body, - const char *content_type); - - std::shared_ptr Options(const char *path); - - std::shared_ptr Options(const char *path, const Headers &headers); - - bool send(const Request &req, Response &res); - - bool send(const std::vector &requests, std::vector &responses); - - void set_keep_alive_max_count(size_t count); - - void follow_location(bool on); - - protected: - bool process_request(Stream &strm, const Request &req, Response &res, bool last_connection, bool &connection_close); - - const std::string host_; - const int port_; - time_t timeout_sec_; - const std::string host_and_port_; - size_t keep_alive_max_count_; - size_t follow_location_; - - private: - socket_t create_client_socket() const; - bool read_response_line(Stream &strm, Response &res); - void write_request(Stream &strm, const Request &req, bool last_connection); - bool redirect(const Request &req, Response &res); - - virtual bool process_and_close_socket( - socket_t sock, size_t request_count, - std::function callback); - - virtual bool is_ssl() const; -}; - -inline void Get(std::vector &requests, const char *path, const Headers &headers) -{ - Request req; - req.method = "GET"; - req.path = path; - req.headers = headers; - requests.emplace_back(std::move(req)); -} - -inline void Get(std::vector &requests, const char *path) -{ - Get(requests, path, Headers()); -} - -inline void Post(std::vector &requests, const char *path, const Headers &headers, const std::string &body, - const char *content_type) -{ - Request req; - req.method = "POST"; - req.path = path; - req.headers = headers; - req.headers.emplace("Content-Type", content_type); - req.body = body; - requests.emplace_back(std::move(req)); -} - -inline void Post(std::vector &requests, const char *path, const std::string &body, const char *content_type) -{ - Post(requests, path, Headers(), body, content_type); -} - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -class SSLSocketStream : public Stream -{ - public: - SSLSocketStream(socket_t sock, SSL *ssl); - virtual ~SSLSocketStream(); - - virtual int read(char *ptr, size_t size); - virtual int write(const char *ptr, size_t size); - virtual int write(const char *ptr); - virtual int write(const std::string &s); - virtual std::string get_remote_addr() const; - - private: - socket_t sock_; - SSL *ssl_; -}; - -class SSLServer : public Server -{ - public: - SSLServer(const char *cert_path, const char *private_key_path, const char *client_ca_cert_file_path = nullptr, - const char *client_ca_cert_dir_path = nullptr); - - virtual ~SSLServer(); - - virtual bool is_valid() const; - - private: - virtual bool process_and_close_socket(socket_t sock); - - SSL_CTX *ctx_; - std::mutex ctx_mutex_; -}; - -class SSLClient : public Client -{ - public: - SSLClient(const char *host, int port = 443, time_t timeout_sec = 300, const char *client_cert_path = nullptr, - const char *client_key_path = nullptr); - - virtual ~SSLClient(); - - virtual bool is_valid() const; - - void set_ca_cert_path(const char *ca_ceert_file_path, const char *ca_cert_dir_path = nullptr); - void enable_server_certificate_verification(bool enabled); - - long get_openssl_verify_result() const; - - SSL_CTX *ssl_context() const noexcept; - - private: - virtual bool process_and_close_socket( - socket_t sock, size_t request_count, - std::function callback); - virtual bool is_ssl() const; - - bool verify_host(X509 *server_cert) const; - bool verify_host_with_subject_alt_name(X509 *server_cert) const; - bool verify_host_with_common_name(X509 *server_cert) const; - bool check_host_name(const char *pattern, size_t pattern_len) const; - - SSL_CTX *ctx_; - std::mutex ctx_mutex_; - std::vector host_components_; - std::string ca_cert_file_path_; - std::string ca_cert_dir_path_; - bool server_certificate_verification_ = false; - long verify_result_ = 0; -}; -#endif - -/* - * Implementation - */ -namespace detail -{ - -inline bool is_hex(char c, int &v) -{ - if(0x20 <= c && isdigit(c)) - { - v = c - '0'; - return true; - } - else if('A' <= c && c <= 'F') - { - v = c - 'A' + 10; - return true; - } - else if('a' <= c && c <= 'f') - { - v = c - 'a' + 10; - return true; - } - return false; -} - -inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, int &val) -{ - if(i >= s.size()) - { - return false; - } - - val = 0; - for(; cnt; i++, cnt--) - { - if(!s[i]) - { - return false; - } - int v = 0; - if(is_hex(s[i], v)) - { - val = val * 16 + v; - } - else - { - return false; - } - } - return true; -} - -inline std::string from_i_to_hex(size_t n) -{ - const char *charset = "0123456789abcdef"; - std::string ret; - do - { - ret = charset[n & 15] + ret; - n >>= 4; - } while(n > 0); - return ret; -} - -inline size_t to_utf8(int code, char *buff) -{ - if(code < 0x0080) - { - buff[0] = (code & 0x7F); - return 1; - } - else if(code < 0x0800) - { - buff[0] = (0xC0 | ((code >> 6) & 0x1F)); - buff[1] = (0x80 | (code & 0x3F)); - return 2; - } - else if(code < 0xD800) - { - buff[0] = (0xE0 | ((code >> 12) & 0xF)); - buff[1] = (0x80 | ((code >> 6) & 0x3F)); - buff[2] = (0x80 | (code & 0x3F)); - return 3; - } - else if(code < 0xE000) - { // D800 - DFFF is invalid... - return 0; - } - else if(code < 0x10000) - { - buff[0] = (0xE0 | ((code >> 12) & 0xF)); - buff[1] = (0x80 | ((code >> 6) & 0x3F)); - buff[2] = (0x80 | (code & 0x3F)); - return 3; - } - else if(code < 0x110000) - { - buff[0] = (0xF0 | ((code >> 18) & 0x7)); - buff[1] = (0x80 | ((code >> 12) & 0x3F)); - buff[2] = (0x80 | ((code >> 6) & 0x3F)); - buff[3] = (0x80 | (code & 0x3F)); - return 4; - } - - // NOTREACHED - return 0; -} - -// NOTE: This code came up with the following stackoverflow post: -// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c -inline std::string base64_encode(const std::string &in) -{ - static const auto lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - std::string out; - out.reserve(in.size()); - - int val = 0; - int valb = -6; - - for(uint8_t c : in) - { - val = (val << 8) + c; - valb += 8; - while(valb >= 0) - { - out.push_back(lookup[(val >> valb) & 0x3F]); - valb -= 6; - } - } - - if(valb > -6) - { - out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); - } - - while(out.size() % 4) - { - out.push_back('='); - } - - return out; -} - -inline bool is_file(const std::string &path) -{ - struct stat st; - return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode); -} - -inline bool is_dir(const std::string &path) -{ - struct stat st; - return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode); -} - -inline bool is_valid_path(const std::string &path) -{ - size_t level = 0; - size_t i = 0; - - // Skip slash - while(i < path.size() && path[i] == '/') - { - i++; - } - - while(i < path.size()) - { - // Read component - auto beg = i; - while(i < path.size() && path[i] != '/') - { - i++; - } - - auto len = i - beg; - assert(len > 0); - - if(!path.compare(beg, len, ".")) - { - ; - } - else if(!path.compare(beg, len, "..")) - { - if(level == 0) - { - return false; - } - level--; - } - else - { - level++; - } - - // Skip slash - while(i < path.size() && path[i] == '/') - { - i++; - } - } - - return true; -} - -inline void read_file(const std::string &path, std::string &out) -{ - std::ifstream fs(path, std::ios_base::binary); - fs.seekg(0, std::ios_base::end); - auto size = fs.tellg(); - fs.seekg(0); - out.resize(static_cast(size)); - fs.read(&out[0], size); -} - -inline std::string file_extension(const std::string &path) -{ - std::smatch m; - auto pat = std::regex("\\.([a-zA-Z0-9]+)$"); - if(std::regex_search(path, m, pat)) - { - return m[1].str(); - } - return std::string(); -} - -template void split(const char *b, const char *e, char d, Fn fn) -{ - int i = 0; - int beg = 0; - - while(e ? (b + i != e) : (b[i] != '\0')) - { - if(b[i] == d) - { - fn(&b[beg], &b[i]); - beg = i + 1; - } - i++; - } - - if(i) - { - fn(&b[beg], &b[i]); - } -} - -// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` -// to store data. The call can set memory on stack for performance. -class stream_line_reader -{ - public: - stream_line_reader(Stream &strm, char *fixed_buffer, size_t fixed_buffer_size) - : strm_(strm), fixed_buffer_(fixed_buffer), fixed_buffer_size_(fixed_buffer_size) - { - } - - const char *ptr() const - { - if(glowable_buffer_.empty()) - { - return fixed_buffer_; - } - else - { - return glowable_buffer_.data(); - } - } - - size_t size() const - { - if(glowable_buffer_.empty()) - { - return fixed_buffer_used_size_; - } - else - { - return glowable_buffer_.size(); - } - } - - bool getline() - { - fixed_buffer_used_size_ = 0; - glowable_buffer_.clear(); - - for(size_t i = 0;; i++) - { - char byte; - auto n = strm_.read(&byte, 1); - - if(n < 0) - { - return false; - } - else if(n == 0) - { - if(i == 0) - { - return false; - } - else - { - break; - } - } - - append(byte); - - if(byte == '\n') - { - break; - } - } - - return true; - } - - private: - void append(char c) - { - if(fixed_buffer_used_size_ < fixed_buffer_size_ - 1) - { - fixed_buffer_[fixed_buffer_used_size_++] = c; - fixed_buffer_[fixed_buffer_used_size_] = '\0'; - } - else - { - if(glowable_buffer_.empty()) - { - assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); - glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); - } - glowable_buffer_ += c; - } - } - - Stream &strm_; - char *fixed_buffer_; - const size_t fixed_buffer_size_; - size_t fixed_buffer_used_size_; - std::string glowable_buffer_; -}; - -inline int close_socket(socket_t sock) -{ -#ifdef _WIN32 - return closesocket(sock); -#else - return close(sock); -#endif -} - -inline int select_read(socket_t sock, time_t sec, time_t usec) -{ -#ifdef CPPHTTPLIB_USE_SELECT - fd_set fds; - FD_ZERO(&fds); - FD_SET(sock, &fds); - - timeval tv; - tv.tv_sec = static_cast(sec); - tv.tv_usec = static_cast(usec); - - return select(static_cast(sock + 1), &fds, nullptr, nullptr, &tv); -#else - struct pollfd pfd_read; - pfd_read.fd = sock; - pfd_read.events = POLLIN; - - auto timeout = static_cast(sec * 1000 + usec / 1000); - - return poll(&pfd_read, 1, timeout); -#endif -} - -inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) -{ -#ifdef CPPHTTPLIB_USE_SELECT - fd_set fdsr; - FD_ZERO(&fdsr); - FD_SET(sock, &fdsr); - - auto fdsw = fdsr; - auto fdse = fdsr; - - timeval tv; - tv.tv_sec = static_cast(sec); - tv.tv_usec = static_cast(usec); - - if(select(static_cast(sock + 1), &fdsr, &fdsw, &fdse, &tv) > 0 && - (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) - { - int error = 0; - socklen_t len = sizeof(error); - return getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&error, &len) >= 0 && !error; - } - return false; -#else - struct pollfd pfd_read; - pfd_read.fd = sock; - pfd_read.events = POLLIN | POLLOUT; - - auto timeout = static_cast(sec * 1000 + usec / 1000); - - if(poll(&pfd_read, 1, timeout) > 0 && pfd_read.revents & (POLLIN | POLLOUT)) - { - int error = 0; - socklen_t len = sizeof(error); - return getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast(&error), &len) >= 0 && !error; - } - return false; -#endif -} - -template -inline bool process_and_close_socket(bool is_client_request, socket_t sock, size_t keep_alive_max_count, T callback) -{ - assert(keep_alive_max_count > 0); - - bool ret = false; - - if(keep_alive_max_count > 1) - { - auto count = keep_alive_max_count; - while(count > 0 && (is_client_request || detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, - CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) - { - SocketStream strm(sock); - auto last_connection = count == 1; - auto connection_close = false; - - ret = callback(strm, last_connection, connection_close); - if(!ret || connection_close) - { - break; - } - - count--; - } - } - else - { - SocketStream strm(sock); - auto dummy_connection_close = false; - ret = callback(strm, true, dummy_connection_close); - } - - close_socket(sock); - return ret; -} - -inline int shutdown_socket(socket_t sock) -{ -#ifdef _WIN32 - return shutdown(sock, SD_BOTH); -#else - return shutdown(sock, SHUT_RDWR); -#endif -} - -template socket_t create_socket(const char *host, int port, Fn fn, int socket_flags = 0) -{ -#ifdef _WIN32 -#define SO_SYNCHRONOUS_NONALERT 0x20 -#define SO_OPENTYPE 0x7008 - - int opt = SO_SYNCHRONOUS_NONALERT; - setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&opt, sizeof(opt)); -#endif - - // Get address info - struct addrinfo hints; - struct addrinfo *result; - - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = socket_flags; - hints.ai_protocol = 0; - - auto service = std::to_string(port); - - if(getaddrinfo(host, service.c_str(), &hints, &result)) - { - return INVALID_SOCKET; - } - - for(auto rp = result; rp; rp = rp->ai_next) - { - // Create a socket -#ifdef _WIN32 - auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT); -#else - auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); -#endif - if(sock == INVALID_SOCKET) - { - continue; - } - -#ifndef _WIN32 - if(fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) - { - continue; - } -#endif - - // Make 'reuse address' option available - int yes = 1; - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&yes), sizeof(yes)); -#ifdef SO_REUSEPORT - setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast(&yes), sizeof(yes)); -#endif - - // bind or connect - if(fn(sock, *rp)) - { - freeaddrinfo(result); - return sock; - } - - close_socket(sock); - } - - freeaddrinfo(result); - return INVALID_SOCKET; -} - -inline void set_nonblocking(socket_t sock, bool nonblocking) -{ -#ifdef _WIN32 - auto flags = nonblocking ? 1UL : 0UL; - ioctlsocket(sock, FIONBIO, &flags); -#else - auto flags = fcntl(sock, F_GETFL, 0); - fcntl(sock, F_SETFL, nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))); -#endif -} - -inline bool is_connection_error() -{ -#ifdef _WIN32 - return WSAGetLastError() != WSAEWOULDBLOCK; -#else - return errno != EINPROGRESS; -#endif -} - -inline std::string get_remote_addr(socket_t sock) -{ - struct sockaddr_storage addr; - socklen_t len = sizeof(addr); - - if(!getpeername(sock, reinterpret_cast(&addr), &len)) - { - char ipstr[NI_MAXHOST]; - - if(!getnameinfo(reinterpret_cast(&addr), len, ipstr, sizeof(ipstr), nullptr, 0, - NI_NUMERICHOST)) - { - return ipstr; - } - } - - return std::string(); -} - -inline const char *find_content_type(const std::string &path) -{ - auto ext = file_extension(path); - if(ext == "txt") - { - return "text/plain"; - } - else if(ext == "html") - { - return "text/html"; - } - else if(ext == "css") - { - return "text/css"; - } - else if(ext == "jpeg" || ext == "jpg") - { - return "image/jpg"; - } - else if(ext == "png") - { - return "image/png"; - } - else if(ext == "gif") - { - return "image/gif"; - } - else if(ext == "svg") - { - return "image/svg+xml"; - } - else if(ext == "ico") - { - return "image/x-icon"; - } - else if(ext == "json") - { - return "application/json"; - } - else if(ext == "pdf") - { - return "application/pdf"; - } - else if(ext == "js") - { - return "application/javascript"; - } - else if(ext == "xml") - { - return "application/xml"; - } - else if(ext == "xhtml") - { - return "application/xhtml+xml"; - } - return nullptr; -} - -inline const char *status_message(int status) -{ - switch(status) - { - case 200: - return "OK"; - case 206: - return "Partial Content"; - case 301: - return "Moved Permanently"; - case 302: - return "Found"; - case 303: - return "See Other"; - case 304: - return "Not Modified"; - case 400: - return "Bad Request"; - case 403: - return "Forbidden"; - case 404: - return "Not Found"; - case 413: - return "Payload Too Large"; - case 414: - return "Request-URI Too Long"; - case 415: - return "Unsupported Media Type"; - case 416: - return "Range Not Satisfiable"; - - default: - case 500: - return "Internal Server Error"; - } -} - -#ifdef CPPHTTPLIB_ZLIB_SUPPORT -inline bool can_compress(const std::string &content_type) -{ - return !content_type.find("text/") || content_type == "image/svg+xml" || content_type == "application/javascript" || - content_type == "application/json" || content_type == "application/xml" || - content_type == "application/xhtml+xml"; -} - -inline bool compress(std::string &content) -{ - z_stream strm; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - - auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY); - if(ret != Z_OK) - { - return false; - } - - strm.avail_in = content.size(); - strm.next_in = const_cast(reinterpret_cast(content.data())); - - std::string compressed; - - const auto bufsiz = 16384; - char buff[bufsiz]; - do - { - strm.avail_out = bufsiz; - strm.next_out = reinterpret_cast(buff); - ret = deflate(&strm, Z_FINISH); - assert(ret != Z_STREAM_ERROR); - compressed.append(buff, bufsiz - strm.avail_out); - } while(strm.avail_out == 0); - - assert(ret == Z_STREAM_END); - assert(strm.avail_in == 0); - - content.swap(compressed); - - deflateEnd(&strm); - return true; -} - -class decompressor -{ - public: - decompressor() - { - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - - // 15 is the value of wbits, which should be at the maximum possible value - // to ensure that any gzip stream can be decoded. The offset of 16 specifies - // that the stream to decompress will be formatted with a gzip wrapper. - is_valid_ = inflateInit2(&strm, 16 + 15) == Z_OK; - } - - ~decompressor() - { - inflateEnd(&strm); - } - - bool is_valid() const - { - return is_valid_; - } - - template bool decompress(const char *data, size_t data_length, T callback) - { - int ret = Z_OK; - - strm.avail_in = data_length; - strm.next_in = const_cast(reinterpret_cast(data)); - - const auto bufsiz = 16384; - char buff[bufsiz]; - do - { - strm.avail_out = bufsiz; - strm.next_out = reinterpret_cast(buff); - - ret = inflate(&strm, Z_NO_FLUSH); - assert(ret != Z_STREAM_ERROR); - switch(ret) - { - case Z_NEED_DICT: - case Z_DATA_ERROR: - case Z_MEM_ERROR: - inflateEnd(&strm); - return false; - } - - if(!callback(buff, bufsiz - strm.avail_out)) - { - return false; - } - } while(strm.avail_out == 0); - - return ret == Z_STREAM_END; - } - - private: - bool is_valid_; - z_stream strm; -}; -#endif - -inline bool has_header(const Headers &headers, const char *key) -{ - return headers.find(key) != headers.end(); -} - -inline const char *get_header_value(const Headers &headers, const char *key, size_t id = 0, const char *def = nullptr) -{ - auto it = headers.find(key); - std::advance(it, id); - if(it != headers.end()) - { - return it->second.c_str(); - } - return def; -} - -inline uint64_t get_header_value_uint64(const Headers &headers, const char *key, int def = 0) -{ - auto it = headers.find(key); - if(it != headers.end()) - { - return std::strtoull(it->second.data(), nullptr, 10); - } - return def; -} - -inline bool read_headers(Stream &strm, Headers &headers) -{ - static std::regex re(R"((.+?):\s*(.+?)\s*\r\n)"); - - const auto bufsiz = 2048; - char buf[bufsiz]; - - stream_line_reader reader(strm, buf, bufsiz); - - for(;;) - { - if(!reader.getline()) - { - return false; - } - if(!strcmp(reader.ptr(), "\r\n")) - { - break; - } - std::cmatch m; - if(std::regex_match(reader.ptr(), m, re)) - { - auto key = std::string(m[1]); - auto val = std::string(m[2]); - headers.emplace(key, val); - } - } - - return true; -} - -typedef std::function ContentReceiverCore; - -inline bool read_content_with_length(Stream &strm, uint64_t len, Progress progress, ContentReceiverCore out) -{ - char buf[CPPHTTPLIB_RECV_BUFSIZ]; - - uint64_t r = 0; - while(r < len) - { - auto read_len = static_cast(len - r); - auto n = strm.read(buf, std::min(read_len, CPPHTTPLIB_RECV_BUFSIZ)); - if(n <= 0) - { - return false; - } - - if(!out(buf, n)) - { - return false; - } - - r += n; - - if(progress) - { - if(!progress(r, len)) - { - return false; - } - } - } - - return true; -} - -inline void skip_content_with_length(Stream &strm, uint64_t len) -{ - char buf[CPPHTTPLIB_RECV_BUFSIZ]; - uint64_t r = 0; - while(r < len) - { - auto read_len = static_cast(len - r); - auto n = strm.read(buf, std::min(read_len, CPPHTTPLIB_RECV_BUFSIZ)); - if(n <= 0) - { - return; - } - r += n; - } -} - -inline bool read_content_without_length(Stream &strm, ContentReceiverCore out) -{ - char buf[CPPHTTPLIB_RECV_BUFSIZ]; - for(;;) - { - auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); - if(n < 0) - { - return false; - } - else if(n == 0) - { - return true; - } - if(!out(buf, n)) - { - return false; - } - } - - return true; -} - -inline bool read_content_chunked(Stream &strm, ContentReceiverCore out) -{ - const auto bufsiz = 16; - char buf[bufsiz]; - - stream_line_reader reader(strm, buf, bufsiz); - - if(!reader.getline()) - { - return false; - } - - auto chunk_len = std::stoi(reader.ptr(), 0, 16); - - while(chunk_len > 0) - { - if(!read_content_with_length(strm, chunk_len, nullptr, out)) - { - return false; - } - - if(!reader.getline()) - { - return false; - } - - if(strcmp(reader.ptr(), "\r\n")) - { - break; - } - - if(!reader.getline()) - { - return false; - } - - chunk_len = std::stoi(reader.ptr(), 0, 16); - } - - if(chunk_len == 0) - { - // Reader terminator after chunks - if(!reader.getline() || strcmp(reader.ptr(), "\r\n")) - return false; - } - - return true; -} - -inline bool is_chunked_transfer_encoding(const Headers &headers) -{ - return !strcasecmp(get_header_value(headers, "Transfer-Encoding", 0, ""), "chunked"); -} - -template -bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, Progress progress, - ContentReceiverCore receiver) -{ - - ContentReceiverCore out = [&](const char *buf, size_t n) { return receiver(buf, n); }; - -#ifdef CPPHTTPLIB_ZLIB_SUPPORT - detail::decompressor decompressor; - - if(!decompressor.is_valid()) - { - status = 500; - return false; - } - - if(x.get_header_value("Content-Encoding") == "gzip") - { - out = [&](const char *buf, size_t n) { - return decompressor.decompress(buf, n, [&](const char *buf, size_t n) { return receiver(buf, n); }); - }; - } -#else - if(x.get_header_value("Content-Encoding") == "gzip") - { - status = 415; - return false; - } -#endif - - auto ret = true; - auto exceed_payload_max_length = false; - - if(is_chunked_transfer_encoding(x.headers)) - { - ret = read_content_chunked(strm, out); - } - else if(!has_header(x.headers, "Content-Length")) - { - ret = read_content_without_length(strm, out); - } - else - { - auto len = get_header_value_uint64(x.headers, "Content-Length", 0); - if(len > payload_max_length) - { - exceed_payload_max_length = true; - skip_content_with_length(strm, len); - ret = false; - } - else if(len > 0) - { - ret = read_content_with_length(strm, len, progress, out); - } - } - - if(!ret) - { - status = exceed_payload_max_length ? 413 : 400; - } - - return ret; -} - -template inline int write_headers(Stream &strm, const T &info, const Headers &headers) -{ - auto write_len = 0; - for(const auto &x : info.headers) - { - auto len = strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); - if(len < 0) - { - return len; - } - write_len += len; - } - for(const auto &x : headers) - { - auto len = strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); - if(len < 0) - { - return len; - } - write_len += len; - } - auto len = strm.write("\r\n"); - if(len < 0) - { - return len; - } - write_len += len; - return write_len; -} - -inline ssize_t write_content(Stream &strm, ContentProvider content_provider, size_t offset, size_t length) -{ - size_t begin_offset = offset; - size_t end_offset = offset + length; - while(offset < end_offset) - { - ssize_t written_length = 0; - content_provider( - offset, end_offset - offset, - [&](const char *d, size_t l) { - offset += l; - written_length = strm.write(d, l); - }, - [&](void) { written_length = -1; }); - if(written_length < 0) - { - return written_length; - } - } - return static_cast(offset - begin_offset); -} - -inline ssize_t write_content_chunked(Stream &strm, ContentProvider content_provider) -{ - size_t offset = 0; - auto data_available = true; - ssize_t total_written_length = 0; - while(data_available) - { - ssize_t written_length = 0; - content_provider( - offset, 0, - [&](const char *d, size_t l) { - data_available = l > 0; - offset += l; - - // Emit chunked response header and footer for each chunk - auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n"; - written_length = strm.write(chunk); - }, - [&](void) { - data_available = false; - written_length = strm.write("0\r\n\r\n"); - }); - - if(written_length < 0) - { - return written_length; - } - total_written_length += written_length; - } - return total_written_length; -} - -template inline bool redirect(T &cli, const Request &req, Response &res, const std::string &path) -{ - Request new_req; - new_req.method = req.method; - new_req.path = path; - new_req.headers = req.headers; - new_req.body = req.body; - new_req.redirect_count = req.redirect_count - 1; - new_req.response_handler = req.response_handler; - new_req.content_receiver = req.content_receiver; - new_req.progress = req.progress; - - Response new_res; - auto ret = cli.send(new_req, new_res); - if(ret) - { - res = new_res; - } - return ret; -} - -inline std::string encode_url(const std::string &s) -{ - std::string result; - - for(auto i = 0; s[i]; i++) - { - switch(s[i]) - { - case ' ': - result += "%20"; - break; - case '+': - result += "%2B"; - break; - case '\r': - result += "%0D"; - break; - case '\n': - result += "%0A"; - break; - case '\'': - result += "%27"; - break; - case ',': - result += "%2C"; - break; - case ':': - result += "%3A"; - break; - case ';': - result += "%3B"; - break; - default: - auto c = static_cast(s[i]); - if(c >= 0x80) - { - result += '%'; - char hex[4]; - size_t len = snprintf(hex, sizeof(hex) - 1, "%02X", c); - assert(len == 2); - result.append(hex, len); - } - else - { - result += s[i]; - } - break; - } - } - - return result; -} - -inline std::string decode_url(const std::string &s) -{ - std::string result; - - for(size_t i = 0; i < s.size(); i++) - { - if(s[i] == '%' && i + 1 < s.size()) - { - if(s[i + 1] == 'u') - { - int val = 0; - if(from_hex_to_i(s, i + 2, 4, val)) - { - // 4 digits Unicode codes - char buff[4]; - size_t len = to_utf8(val, buff); - if(len > 0) - { - result.append(buff, len); - } - i += 5; // 'u0000' - } - else - { - result += s[i]; - } - } - else - { - int val = 0; - if(from_hex_to_i(s, i + 1, 2, val)) - { - // 2 digits hex codes - result += val; - i += 2; // '00' - } - else - { - result += s[i]; - } - } - } - else if(s[i] == '+') - { - result += ' '; - } - else - { - result += s[i]; - } - } - - return result; -} - -inline void parse_query_text(const std::string &s, Params ¶ms) -{ - split(&s[0], &s[s.size()], '&', [&](const char *b, const char *e) { - std::string key; - std::string val; - split(b, e, '=', [&](const char *b, const char *e) { - if(key.empty()) - { - key.assign(b, e); - } - else - { - val.assign(b, e); - } - }); - params.emplace(key, decode_url(val)); - }); -} - -inline bool parse_multipart_boundary(const std::string &content_type, std::string &boundary) -{ - auto pos = content_type.find("boundary="); - if(pos == std::string::npos) - { - return false; - } - - boundary = content_type.substr(pos + 9); - return true; -} - -inline bool parse_multipart_formdata(const std::string &boundary, const std::string &body, MultipartFiles &files) -{ - static std::string dash = "--"; - static std::string crlf = "\r\n"; - - static std::regex re_content_type("Content-Type: (.*?)", std::regex_constants::icase); - - static std::regex re_content_disposition("Content-Disposition: form-data; name=\"(.*?)\"(?:; filename=\"(.*?)\")?", - std::regex_constants::icase); - - auto dash_boundary = dash + boundary; - - auto pos = body.find(dash_boundary); - if(pos != 0) - { - return false; - } - - pos += dash_boundary.size(); - - auto next_pos = body.find(crlf, pos); - if(next_pos == std::string::npos) - { - return false; - } - - pos = next_pos + crlf.size(); - - while(pos < body.size()) - { - next_pos = body.find(crlf, pos); - if(next_pos == std::string::npos) - { - return false; - } - - std::string name; - MultipartFile file; - - auto header = body.substr(pos, (next_pos - pos)); - - while(pos != next_pos) - { - std::smatch m; - if(std::regex_match(header, m, re_content_type)) - { - file.content_type = m[1]; - } - else if(std::regex_match(header, m, re_content_disposition)) - { - name = m[1]; - file.filename = m[2]; - } - - pos = next_pos + crlf.size(); - - next_pos = body.find(crlf, pos); - if(next_pos == std::string::npos) - { - return false; - } - - header = body.substr(pos, (next_pos - pos)); - } - - pos = next_pos + crlf.size(); - - next_pos = body.find(crlf + dash_boundary, pos); - - if(next_pos == std::string::npos) - { - return false; - } - - file.offset = pos; - file.length = next_pos - pos; - - pos = next_pos + crlf.size() + dash_boundary.size(); - - next_pos = body.find(crlf, pos); - if(next_pos == std::string::npos) - { - return false; - } - - files.emplace(name, file); - - pos = next_pos + crlf.size(); - } - - return true; -} - -inline bool parse_range_header(const std::string &s, Ranges &ranges) -{ - try - { - static auto re = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); - std::smatch m; - if(std::regex_match(s, m, re)) - { - auto pos = m.position(1); - auto len = m.length(1); - detail::split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) { - static auto re = std::regex(R"(\s*(\d*)-(\d*))"); - std::cmatch m; - if(std::regex_match(b, e, m, re)) - { - ssize_t first = -1; - if(!m.str(1).empty()) - { - first = static_cast(std::stoll(m.str(1))); - } - - ssize_t last = -1; - if(!m.str(2).empty()) - { - last = static_cast(std::stoll(m.str(2))); - } - - if(first != -1 && last != -1 && first > last) - { - throw std::runtime_error("invalid range error"); - } - ranges.emplace_back(std::make_pair(first, last)); - } - }); - return true; - } - return false; - } - catch(...) - { - return false; - } -} - -inline std::string to_lower(const char *beg, const char *end) -{ - std::string out; - auto it = beg; - while(it != end) - { - out += ::tolower(*it); - it++; - } - return out; -} - -inline std::string make_multipart_data_boundary() -{ - static const char data[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - - std::random_device seed_gen; - std::mt19937 engine(seed_gen()); - - std::string result = "--cpp-httplib-multipart-data-"; - - for(auto i = 0; i < 16; i++) - { - result += data[engine() % (sizeof(data) - 1)]; - } - - return result; -} - -inline std::pair get_range_offset_and_length(const Request &req, size_t content_length, size_t index) -{ - auto r = req.ranges[index]; - - if(r.first == -1 && r.second == -1) - { - return std::make_pair(0, content_length); - } - - if(r.first == -1) - { - r.first = content_length - r.second; - r.second = content_length - 1; - } - - if(r.second == -1) - { - r.second = content_length - 1; - } - - return std::make_pair(r.first, r.second - r.first + 1); -} - -inline std::string make_content_range_header_field(size_t offset, size_t length, size_t content_length) -{ - std::string field = "bytes "; - field += std::to_string(offset); - field += "-"; - field += std::to_string(offset + length - 1); - field += "/"; - field += std::to_string(content_length); - return field; -} - -template -bool process_multipart_ranges_data(const Request &req, Response &res, const std::string &boundary, - const std::string &content_type, SToken stoken, CToken ctoken, Content content) -{ - for(size_t i = 0; i < req.ranges.size(); i++) - { - ctoken("--"); - stoken(boundary); - ctoken("\r\n"); - if(!content_type.empty()) - { - ctoken("Content-Type: "); - stoken(content_type); - ctoken("\r\n"); - } - - auto offsets = detail::get_range_offset_and_length(req, res.body.size(), i); - auto offset = offsets.first; - auto length = offsets.second; - - ctoken("Content-Range: "); - stoken(make_content_range_header_field(offset, length, res.body.size())); - ctoken("\r\n"); - ctoken("\r\n"); - if(!content(offset, length)) - { - return false; - } - ctoken("\r\n"); - } - - ctoken("--"); - stoken(boundary); - ctoken("--\r\n"); - - return true; -} - -inline std::string make_multipart_ranges_data(const Request &req, Response &res, const std::string &boundary, - const std::string &content_type) -{ - std::string data; - - process_multipart_ranges_data( - req, res, boundary, content_type, [&](const std::string &token) { data += token; }, - [&](const char *token) { data += token; }, - [&](size_t offset, size_t length) { - data += res.body.substr(offset, length); - return true; - }); - - return data; -} - -inline size_t get_multipart_ranges_data_length(const Request &req, Response &res, const std::string &boundary, - const std::string &content_type) -{ - size_t data_length = 0; - - process_multipart_ranges_data( - req, res, boundary, content_type, [&](const std::string &token) { data_length += token.size(); }, - [&](const char *token) { data_length += strlen(token); }, - [&](size_t /*offset*/, size_t length) { - data_length += length; - return true; - }); - - return data_length; -} - -inline bool write_multipart_ranges_data(Stream &strm, const Request &req, Response &res, const std::string &boundary, - const std::string &content_type) -{ - return process_multipart_ranges_data( - req, res, boundary, content_type, [&](const std::string &token) { strm.write(token); }, - [&](const char *token) { strm.write(token); }, - [&](size_t offset, size_t length) { - return detail::write_content(strm, res.content_provider, offset, length) >= 0; - }); -} - -inline std::pair get_range_offset_and_length(const Request &req, const Response &res, size_t index) -{ - auto r = req.ranges[index]; - - if(r.second == -1) - { - r.second = res.content_provider_resource_length - 1; - } - - return std::make_pair(r.first, r.second - r.first + 1); -} - -#ifdef _WIN32 -class WSInit -{ - public: - WSInit() - { - WSADATA wsaData; - WSAStartup(0x0002, &wsaData); - } - - ~WSInit() - { - WSACleanup(); - } -}; - -static WSInit wsinit_; -#endif - -} // namespace detail - -// Header utilities -inline std::pair make_range_header(Ranges ranges) -{ - std::string field = "bytes="; - auto i = 0; - for(auto r : ranges) - { - if(i != 0) - { - field += ", "; - } - if(r.first != -1) - { - field += std::to_string(r.first); - } - field += '-'; - if(r.second != -1) - { - field += std::to_string(r.second); - } - i++; - } - return std::make_pair("Range", field); -} - -inline std::pair make_basic_authentication_header(const std::string &username, - const std::string &password) -{ - auto field = "Basic " + detail::base64_encode(username + ":" + password); - return std::make_pair("Authorization", field); -} - -// Request implementation -inline bool Request::has_header(const char *key) const -{ - return detail::has_header(headers, key); -} - -inline std::string Request::get_header_value(const char *key, size_t id) const -{ - return detail::get_header_value(headers, key, id, ""); -} - -inline size_t Request::get_header_value_count(const char *key) const -{ - auto r = headers.equal_range(key); - return std::distance(r.first, r.second); -} - -inline void Request::set_header(const char *key, const char *val) -{ - headers.emplace(key, val); -} - -inline void Request::set_header(const char *key, const std::string &val) -{ - headers.emplace(key, val); -} - -inline bool Request::has_param(const char *key) const -{ - return params.find(key) != params.end(); -} - -inline std::string Request::get_param_value(const char *key, size_t id) const -{ - auto it = params.find(key); - std::advance(it, id); - if(it != params.end()) - { - return it->second; - } - return std::string(); -} - -inline size_t Request::get_param_value_count(const char *key) const -{ - auto r = params.equal_range(key); - return std::distance(r.first, r.second); -} - -inline bool Request::has_file(const char *key) const -{ - return files.find(key) != files.end(); -} - -inline MultipartFile Request::get_file_value(const char *key) const -{ - auto it = files.find(key); - if(it != files.end()) - { - return it->second; - } - return MultipartFile(); -} - -// Response implementation -inline bool Response::has_header(const char *key) const -{ - return headers.find(key) != headers.end(); -} - -inline std::string Response::get_header_value(const char *key, size_t id) const -{ - return detail::get_header_value(headers, key, id, ""); -} - -inline size_t Response::get_header_value_count(const char *key) const -{ - auto r = headers.equal_range(key); - return std::distance(r.first, r.second); -} - -inline void Response::set_header(const char *key, const char *val) -{ - headers.emplace(key, val); -} - -inline void Response::set_header(const char *key, const std::string &val) -{ - headers.emplace(key, val); -} - -inline void Response::set_redirect(const char *url) -{ - set_header("Location", url); - status = 302; -} - -inline void Response::set_content(const char *s, size_t n, const char *content_type) -{ - body.assign(s, n); - set_header("Content-Type", content_type); -} - -inline void Response::set_content(const std::string &s, const char *content_type) -{ - body = s; - set_header("Content-Type", content_type); -} - -inline void Response::set_content_provider(size_t length, - std::function provider, - std::function resource_releaser) -{ - assert(length > 0); - content_provider_resource_length = length; - content_provider = [provider](size_t offset, size_t length, DataSink sink, Done) { - provider(offset, length, sink); - }; - content_provider_resource_releaser = resource_releaser; -} - -inline void Response::set_chunked_content_provider( - std::function provider, std::function resource_releaser) -{ - content_provider_resource_length = 0; - content_provider = [provider](size_t offset, size_t, DataSink sink, Done done) { provider(offset, sink, done); }; - content_provider_resource_releaser = resource_releaser; -} - -// Rstream implementation -template inline int Stream::write_format(const char *fmt, const Args &...args) -{ - const auto bufsiz = 2048; - char buf[bufsiz]; - -#if defined(_MSC_VER) && _MSC_VER < 1900 - auto n = _snprintf_s(buf, bufsiz, bufsiz - 1, fmt, args...); -#else - auto n = snprintf(buf, bufsiz - 1, fmt, args...); -#endif - if(n <= 0) - { - return n; - } - - if(n >= bufsiz - 1) - { - std::vector glowable_buf(bufsiz); - - while(n >= static_cast(glowable_buf.size() - 1)) - { - glowable_buf.resize(glowable_buf.size() * 2); -#if defined(_MSC_VER) && _MSC_VER < 1900 - n = _snprintf_s(&glowable_buf[0], glowable_buf.size(), glowable_buf.size() - 1, fmt, args...); -#else - n = snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...); -#endif - } - return write(&glowable_buf[0], n); - } - else - { - return write(buf, n); - } -} - -// Socket stream implementation -inline SocketStream::SocketStream(socket_t sock) : sock_(sock) -{ -} - -inline SocketStream::~SocketStream() -{ -} - -inline int SocketStream::read(char *ptr, size_t size) -{ - if(detail::select_read(sock_, CPPHTTPLIB_READ_TIMEOUT_SECOND, CPPHTTPLIB_READ_TIMEOUT_USECOND) > 0) - { - return recv(sock_, ptr, static_cast(size), 0); - } - return -1; -} - -inline int SocketStream::write(const char *ptr, size_t size) -{ - return send(sock_, ptr, static_cast(size), 0); -} - -inline int SocketStream::write(const char *ptr) -{ - return write(ptr, strlen(ptr)); -} - -inline int SocketStream::write(const std::string &s) -{ - return write(s.data(), s.size()); -} - -inline std::string SocketStream::get_remote_addr() const -{ - return detail::get_remote_addr(sock_); -} - -// Buffer stream implementation -inline int BufferStream::read(char *ptr, size_t size) -{ -#if defined(_MSC_VER) && _MSC_VER < 1900 - return static_cast(buffer._Copy_s(ptr, size, size)); -#else - return static_cast(buffer.copy(ptr, size)); -#endif -} - -inline int BufferStream::write(const char *ptr, size_t size) -{ - buffer.append(ptr, size); - return static_cast(size); -} - -inline int BufferStream::write(const char *ptr) -{ - return write(ptr, strlen(ptr)); -} - -inline int BufferStream::write(const std::string &s) -{ - return write(s.data(), s.size()); -} - -inline std::string BufferStream::get_remote_addr() const -{ - return ""; -} - -inline const std::string &BufferStream::get_buffer() const -{ - return buffer; -} - -// HTTP server implementation -inline Server::Server() - : keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), payload_max_length_(CPPHTTPLIB_PAYLOAD_MAX_LENGTH), - is_running_(false), svr_sock_(INVALID_SOCKET) -{ -#ifndef _WIN32 - signal(SIGPIPE, SIG_IGN); -#endif - new_task_queue = [] { -#if CPPHTTPLIB_THREAD_POOL_COUNT > 0 - return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); -#else - return new Threads(); -#endif - }; -} - -inline Server::~Server() -{ -} - -inline Server &Server::Get(const char *pattern, Handler handler) -{ - get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); - return *this; -} - -inline Server &Server::Post(const char *pattern, Handler handler) -{ - post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); - return *this; -} - -inline Server &Server::Put(const char *pattern, Handler handler) -{ - put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); - return *this; -} - -inline Server &Server::Patch(const char *pattern, Handler handler) -{ - patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); - return *this; -} - -inline Server &Server::Delete(const char *pattern, Handler handler) -{ - delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); - return *this; -} - -inline Server &Server::Options(const char *pattern, Handler handler) -{ - options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); - return *this; -} - -inline bool Server::set_base_dir(const char *path) -{ - if(detail::is_dir(path)) - { - base_dir_ = path; - return true; - } - return false; -} - -inline void Server::set_file_request_handler(Handler handler) -{ - file_request_handler_ = handler; -} - -inline void Server::set_error_handler(Handler handler) -{ - error_handler_ = handler; -} - -inline void Server::set_logger(Logger logger) -{ - logger_ = logger; -} - -inline void Server::set_keep_alive_max_count(size_t count) -{ - keep_alive_max_count_ = count; -} - -inline void Server::set_payload_max_length(size_t length) -{ - payload_max_length_ = length; -} - -inline int Server::bind_to_any_port(const char *host, int socket_flags) -{ - return bind_internal(host, 0, socket_flags); -} - -inline bool Server::listen_after_bind() -{ - return listen_internal(); -} - -inline bool Server::listen(const char *host, int port, int socket_flags) -{ - if(bind_internal(host, port, socket_flags) < 0) - return false; - return listen_internal(); -} - -inline bool Server::is_running() const -{ - return is_running_; -} - -inline void Server::stop() -{ - if(is_running_) - { - assert(svr_sock_ != INVALID_SOCKET); - std::atomic sock(svr_sock_.exchange(INVALID_SOCKET)); - detail::shutdown_socket(sock); - detail::close_socket(sock); - } -} - -inline bool Server::parse_request_line(const char *s, Request &req) -{ - static std::regex re("(GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS) " - "(([^?]+)(?:\\?(.+?))?) (HTTP/1\\.[01])\r\n"); - - std::cmatch m; - if(std::regex_match(s, m, re)) - { - req.version = std::string(m[5]); - req.method = std::string(m[1]); - req.target = std::string(m[2]); - req.path = detail::decode_url(m[3]); - - // Parse query text - auto len = std::distance(m[4].first, m[4].second); - if(len > 0) - { - detail::parse_query_text(m[4], req.params); - } - - return true; - } - - return false; -} - -inline bool Server::write_response(Stream &strm, bool last_connection, const Request &req, Response &res) -{ - assert(res.status != -1); - - if(400 <= res.status && error_handler_) - { - error_handler_(req, res); - } - - // Response line - if(!strm.write_format("HTTP/1.1 %d %s\r\n", res.status, detail::status_message(res.status))) - { - return false; - } - - // Headers - if(last_connection || req.get_header_value("Connection") == "close") - { - res.set_header("Connection", "close"); - } - - if(!last_connection && req.get_header_value("Connection") == "Keep-Alive") - { - res.set_header("Connection", "Keep-Alive"); - } - - if(!res.has_header("Content-Type")) - { - res.set_header("Content-Type", "text/plain"); - } - - if(!res.has_header("Accept-Ranges")) - { - res.set_header("Accept-Ranges", "bytes"); - } - - std::string content_type; - std::string boundary; - - if(req.ranges.size() > 1) - { - boundary = detail::make_multipart_data_boundary(); - - auto it = res.headers.find("Content-Type"); - if(it != res.headers.end()) - { - content_type = it->second; - res.headers.erase(it); - } - - res.headers.emplace("Content-Type", "multipart/byteranges; boundary=" + boundary); - } - - if(res.body.empty()) - { - if(res.content_provider_resource_length > 0) - { - size_t length = 0; - if(req.ranges.empty()) - { - length = res.content_provider_resource_length; - } - else if(req.ranges.size() == 1) - { - auto offsets = detail::get_range_offset_and_length(req, res.content_provider_resource_length, 0); - auto offset = offsets.first; - length = offsets.second; - auto content_range = - detail::make_content_range_header_field(offset, length, res.content_provider_resource_length); - res.set_header("Content-Range", content_range); - } - else - { - length = detail::get_multipart_ranges_data_length(req, res, boundary, content_type); - } - res.set_header("Content-Length", std::to_string(length)); - } - else - { - if(res.content_provider) - { - res.set_header("Transfer-Encoding", "chunked"); - } - else - { - res.set_header("Content-Length", "0"); - } - } - } - else - { - if(req.ranges.empty()) - { - ; - } - else if(req.ranges.size() == 1) - { - auto offsets = detail::get_range_offset_and_length(req, res.body.size(), 0); - auto offset = offsets.first; - auto length = offsets.second; - auto content_range = detail::make_content_range_header_field(offset, length, res.body.size()); - res.set_header("Content-Range", content_range); - res.body = res.body.substr(offset, length); - } - else - { - res.body = detail::make_multipart_ranges_data(req, res, boundary, content_type); - } - -#ifdef CPPHTTPLIB_ZLIB_SUPPORT - // TODO: 'Accpet-Encoding' has gzip, not gzip;q=0 - const auto &encodings = req.get_header_value("Accept-Encoding"); - if(encodings.find("gzip") != std::string::npos && detail::can_compress(res.get_header_value("Content-Type"))) - { - if(detail::compress(res.body)) - { - res.set_header("Content-Encoding", "gzip"); - } - } -#endif - - auto length = std::to_string(res.body.size()); - res.set_header("Content-Length", length); - } - - if(!detail::write_headers(strm, res, Headers())) - { - return false; - } - - // Body - if(req.method != "HEAD") - { - if(!res.body.empty()) - { - if(!strm.write(res.body)) - { - return false; - } - } - else if(res.content_provider) - { - if(!write_content_with_provider(strm, req, res, boundary, content_type)) - { - return false; - } - } - } - - // Log - if(logger_) - { - logger_(req, res); - } - - return true; -} - -inline bool Server::write_content_with_provider(Stream &strm, const Request &req, Response &res, - const std::string &boundary, const std::string &content_type) -{ - if(res.content_provider_resource_length) - { - if(req.ranges.empty()) - { - if(detail::write_content(strm, res.content_provider, 0, res.content_provider_resource_length) < 0) - { - return false; - } - } - else if(req.ranges.size() == 1) - { - auto offsets = detail::get_range_offset_and_length(req, res.content_provider_resource_length, 0); - auto offset = offsets.first; - auto length = offsets.second; - if(detail::write_content(strm, res.content_provider, offset, length) < 0) - { - return false; - } - } - else - { - if(!detail::write_multipart_ranges_data(strm, req, res, boundary, content_type)) - { - return false; - } - } - } - else - { - if(detail::write_content_chunked(strm, res.content_provider) < 0) - { - return false; - } - } - return true; -} - -inline bool Server::handle_file_request(Request &req, Response &res) -{ - if(!base_dir_.empty() && detail::is_valid_path(req.path)) - { - std::string path = base_dir_ + req.path; - - if(!path.empty() && path.back() == '/') - { - path += "index.html"; - } - - if(detail::is_file(path)) - { - detail::read_file(path, res.body); - auto type = detail::find_content_type(path); - if(type) - { - res.set_header("Content-Type", type); - } - res.status = 200; - if(file_request_handler_) - { - file_request_handler_(req, res); - } - return true; - } - } - - return false; -} - -inline socket_t Server::create_server_socket(const char *host, int port, int socket_flags) const -{ - return detail::create_socket( - host, port, - [](socket_t sock, struct addrinfo &ai) -> bool { - if(::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) - { - return false; - } - if(::listen(sock, 5)) - { // Listen through 5 channels - return false; - } - return true; - }, - socket_flags); -} - -inline int Server::bind_internal(const char *host, int port, int socket_flags) -{ - if(!is_valid()) - { - return -1; - } - - svr_sock_ = create_server_socket(host, port, socket_flags); - if(svr_sock_ == INVALID_SOCKET) - { - return -1; - } - - if(port == 0) - { - struct sockaddr_storage address; - socklen_t len = sizeof(address); - if(getsockname(svr_sock_, reinterpret_cast(&address), &len) == -1) - { - return -1; - } - if(address.ss_family == AF_INET) - { - return ntohs(reinterpret_cast(&address)->sin_port); - } - else if(address.ss_family == AF_INET6) - { - return ntohs(reinterpret_cast(&address)->sin6_port); - } - else - { - return -1; - } - } - else - { - return port; - } -} - -inline bool Server::listen_internal() -{ - auto ret = true; - is_running_ = true; - - { - std::unique_ptr task_queue(new_task_queue()); - - for(;;) - { - if(svr_sock_ == INVALID_SOCKET) - { - // The server socket was closed by 'stop' method. - break; - } - - auto val = detail::select_read(svr_sock_, 0, 100000); - - if(val == 0) - { // Timeout - continue; - } - - socket_t sock = accept(svr_sock_, nullptr, nullptr); - - if(sock == INVALID_SOCKET) - { - if(errno == EMFILE) - { - // The per-process limit of open file descriptors has been reached. - // Try to accept new connections after a short sleep. - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - continue; - } - if(svr_sock_ != INVALID_SOCKET) - { - detail::close_socket(svr_sock_); - ret = false; - } - else - { - ; // The server socket was closed by user. - } - break; - } - - task_queue->enqueue([=]() { process_and_close_socket(sock); }); - } - - task_queue->shutdown(); - } - - is_running_ = false; - return ret; -} - -inline bool Server::routing(Request &req, Response &res) -{ - if(req.method == "GET" && handle_file_request(req, res)) - { - return true; - } - - if(req.method == "GET" || req.method == "HEAD") - { - return dispatch_request(req, res, get_handlers_); - } - else if(req.method == "POST") - { - return dispatch_request(req, res, post_handlers_); - } - else if(req.method == "PUT") - { - return dispatch_request(req, res, put_handlers_); - } - else if(req.method == "PATCH") - { - return dispatch_request(req, res, patch_handlers_); - } - else if(req.method == "DELETE") - { - return dispatch_request(req, res, delete_handlers_); - } - else if(req.method == "OPTIONS") - { - return dispatch_request(req, res, options_handlers_); - } - return false; -} - -inline bool Server::dispatch_request(Request &req, Response &res, Handlers &handlers) -{ - for(const auto &x : handlers) - { - const auto &pattern = x.first; - const auto &handler = x.second; - - if(std::regex_match(req.path, req.matches, pattern)) - { - handler(req, res); - return true; - } - } - return false; -} - -inline bool Server::process_request(Stream &strm, bool last_connection, bool &connection_close, - std::function setup_request) -{ - const auto bufsiz = 2048; - char buf[bufsiz]; - - detail::stream_line_reader reader(strm, buf, bufsiz); - - // Connection has been closed on client - if(!reader.getline()) - { - return false; - } - - Request req; - Response res; - - res.version = "HTTP/1.1"; - - // Check if the request URI doesn't exceed the limit - if(reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) - { - Headers dummy; - detail::read_headers(strm, dummy); - res.status = 414; - return write_response(strm, last_connection, req, res); - } - - // Request line and headers - if(!parse_request_line(reader.ptr(), req) || !detail::read_headers(strm, req.headers)) - { - res.status = 400; - return write_response(strm, last_connection, req, res); - } - - if(req.get_header_value("Connection") == "close") - { - connection_close = true; - } - - if(req.version == "HTTP/1.0" && req.get_header_value("Connection") != "Keep-Alive") - { - connection_close = true; - } - - req.set_header("REMOTE_ADDR", strm.get_remote_addr()); - - // Body - if(req.method == "POST" || req.method == "PUT" || req.method == "PATCH") - { - if(!detail::read_content(strm, req, payload_max_length_, res.status, Progress(), - [&](const char *buf, size_t n) { - if(req.body.size() + n > req.body.max_size()) - { - return false; - } - req.body.append(buf, n); - return true; - })) - { - return write_response(strm, last_connection, req, res); - } - - const auto &content_type = req.get_header_value("Content-Type"); - - if(!content_type.find("application/x-www-form-urlencoded")) - { - detail::parse_query_text(req.body, req.params); - } - else if(!content_type.find("multipart/form-data")) - { - std::string boundary; - if(!detail::parse_multipart_boundary(content_type, boundary) || - !detail::parse_multipart_formdata(boundary, req.body, req.files)) - { - res.status = 400; - return write_response(strm, last_connection, req, res); - } - } - } - - if(req.has_header("Range")) - { - const auto &range_header_value = req.get_header_value("Range"); - if(!detail::parse_range_header(range_header_value, req.ranges)) - { - // TODO: error - } - } - - if(setup_request) - { - setup_request(req); - } - - if(routing(req, res)) - { - if(res.status == -1) - { - res.status = req.ranges.empty() ? 200 : 206; - } - } - else - { - res.status = 404; - } - - return write_response(strm, last_connection, req, res); -} - -inline bool Server::is_valid() const -{ - return true; -} - -inline bool Server::process_and_close_socket(socket_t sock) -{ - return detail::process_and_close_socket( - false, sock, keep_alive_max_count_, [this](Stream &strm, bool last_connection, bool &connection_close) { - return process_request(strm, last_connection, connection_close, nullptr); - }); -} - -// HTTP client implementation -inline Client::Client(const char *host, int port, time_t timeout_sec) - : host_(host), port_(port), timeout_sec_(timeout_sec), host_and_port_(host_ + ":" + std::to_string(port_)), - keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), follow_location_(false) -{ -} - -inline Client::~Client() -{ -} - -inline bool Client::is_valid() const -{ - return true; -} - -inline socket_t Client::create_client_socket() const -{ - return detail::create_socket(host_.c_str(), port_, [=](socket_t sock, struct addrinfo &ai) -> bool { - detail::set_nonblocking(sock, true); - - auto ret = connect(sock, ai.ai_addr, static_cast(ai.ai_addrlen)); - if(ret < 0) - { - if(detail::is_connection_error() || !detail::wait_until_socket_is_ready(sock, timeout_sec_, 0)) - { - detail::close_socket(sock); - return false; - } - } - - detail::set_nonblocking(sock, false); - return true; - }); -} - -inline bool Client::read_response_line(Stream &strm, Response &res) -{ - const auto bufsiz = 2048; - char buf[bufsiz]; - - detail::stream_line_reader reader(strm, buf, bufsiz); - - if(!reader.getline()) - { - return false; - } - - const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .*\r\n"); - - std::cmatch m; - if(std::regex_match(reader.ptr(), m, re)) - { - res.version = std::string(m[1]); - res.status = std::stoi(std::string(m[2])); - } - - return true; -} - -inline bool Client::send(const Request &req, Response &res) -{ - if(req.path.empty()) - { - return false; - } - - auto sock = create_client_socket(); - if(sock == INVALID_SOCKET) - { - return false; - } - - auto ret = process_and_close_socket(sock, 1, [&](Stream &strm, bool last_connection, bool &connection_close) { - return process_request(strm, req, res, last_connection, connection_close); - }); - - if(ret && follow_location_ && (300 < res.status && res.status < 400)) - { - ret = redirect(req, res); - } - - return ret; -} - -inline bool Client::send(const std::vector &requests, std::vector &responses) -{ - size_t i = 0; - while(i < requests.size()) - { - auto sock = create_client_socket(); - if(sock == INVALID_SOCKET) - { - return false; - } - - if(!process_and_close_socket(sock, requests.size() - i, - [&](Stream &strm, bool last_connection, bool &connection_close) -> bool { - auto &req = requests[i]; - auto res = Response(); - i++; - - if(req.path.empty()) - { - return false; - } - auto ret = process_request(strm, req, res, last_connection, connection_close); - - if(ret && follow_location_ && (300 < res.status && res.status < 400)) - { - ret = redirect(req, res); - } - - if(ret) - { - responses.emplace_back(std::move(res)); - } - - return ret; - })) - { - return false; - } - } - - return true; -} - -inline bool Client::redirect(const Request &req, Response &res) -{ - if(req.redirect_count == 0) - { - return false; - } - - auto location = res.get_header_value("location"); - if(location.empty()) - { - return false; - } - - std::regex re(R"(^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*(?:\?[^#]*)?)(?:#.*)?)"); - - auto scheme = is_ssl() ? "https" : "http"; - - std::smatch m; - if(regex_match(location, m, re)) - { - auto next_scheme = m[1].str(); - auto next_host = m[2].str(); - auto next_path = m[3].str(); - if(next_host.empty()) - { - next_host = host_; - } - if(next_path.empty()) - { - next_path = "/"; - } - - if(next_scheme == scheme && next_host == host_) - { - return detail::redirect(*this, req, res, next_path); - } - else - { - if(next_scheme == "https") - { -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - SSLClient cli(next_host.c_str()); - cli.follow_location(true); - return detail::redirect(cli, req, res, next_path); -#else - return false; -#endif - } - else - { - Client cli(next_host.c_str()); - cli.follow_location(true); - return detail::redirect(cli, req, res, next_path); - } - } - } - return false; -} - -inline void Client::write_request(Stream &strm, const Request &req, bool last_connection) -{ - BufferStream bstrm; - - // Request line - auto path = detail::encode_url(req.path); - - bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); - - // Additonal headers - Headers headers; - if(last_connection) - { - headers.emplace("Connection", "close"); - } - - if(!req.has_header("Host")) - { - if(is_ssl()) - { - if(port_ == 443) - { - headers.emplace("Host", host_); - } - else - { - headers.emplace("Host", host_and_port_); - } - } - else - { - if(port_ == 80) - { - headers.emplace("Host", host_); - } - else - { - headers.emplace("Host", host_and_port_); - } - } - } - - if(!req.has_header("Accept")) - { - headers.emplace("Accept", "*/*"); - } - - if(!req.has_header("User-Agent")) - { - headers.emplace("User-Agent", "cpp-httplib/0.2"); - } - - if(req.body.empty()) - { - if(req.method == "POST" || req.method == "PUT" || req.method == "PATCH") - { - headers.emplace("Content-Length", "0"); - } - } - else - { - if(!req.has_header("Content-Type")) - { - headers.emplace("Content-Type", "text/plain"); - } - - if(!req.has_header("Content-Length")) - { - auto length = std::to_string(req.body.size()); - headers.emplace("Content-Length", length); - } - } - - detail::write_headers(bstrm, req, headers); - - // Body - if(!req.body.empty()) - { - bstrm.write(req.body); - } - - // Flush buffer - auto &data = bstrm.get_buffer(); - strm.write(data.data(), data.size()); -} - -inline bool Client::process_request(Stream &strm, const Request &req, Response &res, bool last_connection, - bool &connection_close) -{ - // Send request - write_request(strm, req, last_connection); - - // Receive response and headers - if(!read_response_line(strm, res) || !detail::read_headers(strm, res.headers)) - { - return false; - } - - if(res.get_header_value("Connection") == "close" || res.version == "HTTP/1.0") - { - connection_close = true; - } - - if(req.response_handler) - { - if(!req.response_handler(res)) - { - return false; - } - } - - // Body - if(req.method != "HEAD") - { - detail::ContentReceiverCore out = [&](const char *buf, size_t n) { - if(res.body.size() + n > res.body.max_size()) - { - return false; - } - res.body.append(buf, n); - return true; - }; - - if(req.content_receiver) - { - auto offset = std::make_shared(); - auto length = get_header_value_uint64(res.headers, "Content-Length", 0); - auto receiver = req.content_receiver; - out = [offset, length, receiver](const char *buf, size_t n) { - auto ret = receiver(buf, n, *offset, length); - (*offset) += n; - return ret; - }; - } - - int dummy_status; - if(!detail::read_content(strm, res, std::numeric_limits::max(), dummy_status, req.progress, out)) - { - return false; - } - } - - return true; -} - -inline bool Client::process_and_close_socket( - socket_t sock, size_t request_count, - std::function callback) -{ - request_count = std::min(request_count, keep_alive_max_count_); - return detail::process_and_close_socket(true, sock, request_count, callback); -} - -inline bool Client::is_ssl() const -{ - return false; -} - -inline std::shared_ptr Client::Get(const char *path) -{ - Progress dummy; - return Get(path, Headers(), dummy); -} - -inline std::shared_ptr Client::Get(const char *path, Progress progress) -{ - return Get(path, Headers(), progress); -} - -inline std::shared_ptr Client::Get(const char *path, const Headers &headers) -{ - Progress dummy; - return Get(path, headers, dummy); -} - -inline std::shared_ptr Client::Get(const char *path, const Headers &headers, Progress progress) -{ - Request req; - req.method = "GET"; - req.path = path; - req.headers = headers; - req.progress = progress; - - auto res = std::make_shared(); - return send(req, *res) ? res : nullptr; -} - -inline std::shared_ptr Client::Get(const char *path, ContentReceiver content_receiver) -{ - Progress dummy; - return Get(path, Headers(), nullptr, content_receiver, dummy); -} - -inline std::shared_ptr Client::Get(const char *path, ContentReceiver content_receiver, Progress progress) -{ - return Get(path, Headers(), nullptr, content_receiver, progress); -} - -inline std::shared_ptr Client::Get(const char *path, const Headers &headers, ContentReceiver content_receiver) -{ - Progress dummy; - return Get(path, headers, nullptr, content_receiver, dummy); -} - -inline std::shared_ptr Client::Get(const char *path, const Headers &headers, ContentReceiver content_receiver, - Progress progress) -{ - return Get(path, headers, nullptr, content_receiver, progress); -} - -inline std::shared_ptr Client::Get(const char *path, const Headers &headers, ResponseHandler response_handler, - ContentReceiver content_receiver) -{ - Progress dummy; - return Get(path, headers, response_handler, content_receiver, dummy); -} - -inline std::shared_ptr Client::Get(const char *path, const Headers &headers, ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress) -{ - Request req; - req.method = "GET"; - req.path = path; - req.headers = headers; - req.response_handler = response_handler; - req.content_receiver = content_receiver; - req.progress = progress; - - auto res = std::make_shared(); - return send(req, *res) ? res : nullptr; -} - -inline std::shared_ptr Client::Head(const char *path) -{ - return Head(path, Headers()); -} - -inline std::shared_ptr Client::Head(const char *path, const Headers &headers) -{ - Request req; - req.method = "HEAD"; - req.headers = headers; - req.path = path; - - auto res = std::make_shared(); - - return send(req, *res) ? res : nullptr; -} - -inline std::shared_ptr Client::Post(const char *path, const std::string &body, const char *content_type) -{ - return Post(path, Headers(), body, content_type); -} - -inline std::shared_ptr Client::Post(const char *path, const Headers &headers, const std::string &body, - const char *content_type) -{ - Request req; - req.method = "POST"; - req.headers = headers; - req.path = path; - - req.headers.emplace("Content-Type", content_type); - req.body = body; - - auto res = std::make_shared(); - - return send(req, *res) ? res : nullptr; -} - -inline std::shared_ptr Client::Post(const char *path, const Params ¶ms) -{ - return Post(path, Headers(), params); -} - -inline std::shared_ptr Client::Post(const char *path, const Headers &headers, const Params ¶ms) -{ - std::string query; - for(auto it = params.begin(); it != params.end(); ++it) - { - if(it != params.begin()) - { - query += "&"; - } - query += it->first; - query += "="; - query += detail::encode_url(it->second); - } - - return Post(path, headers, query, "application/x-www-form-urlencoded"); -} - -inline std::shared_ptr Client::Post(const char *path, const MultipartFormDataItems &items) -{ - return Post(path, Headers(), items); -} - -inline std::shared_ptr Client::Post(const char *path, const Headers &headers, - const MultipartFormDataItems &items) -{ - Request req; - req.method = "POST"; - req.headers = headers; - req.path = path; - - auto boundary = detail::make_multipart_data_boundary(); - - req.headers.emplace("Content-Type", "multipart/form-data; boundary=" + boundary); - - for(const auto &item : items) - { - req.body += "--" + boundary + "\r\n"; - req.body += "Content-Disposition: form-data; name=\"" + item.name + "\""; - if(!item.filename.empty()) - { - req.body += "; filename=\"" + item.filename + "\""; - } - req.body += "\r\n"; - if(!item.content_type.empty()) - { - req.body += "Content-Type: " + item.content_type + "\r\n"; - } - req.body += "\r\n"; - req.body += item.content + "\r\n"; - } - - req.body += "--" + boundary + "--\r\n"; - - auto res = std::make_shared(); - - return send(req, *res) ? res : nullptr; -} - -inline std::shared_ptr Client::Put(const char *path, const std::string &body, const char *content_type) -{ - return Put(path, Headers(), body, content_type); -} - -inline std::shared_ptr Client::Put(const char *path, const Headers &headers, const std::string &body, - const char *content_type) -{ - Request req; - req.method = "PUT"; - req.headers = headers; - req.path = path; - - req.headers.emplace("Content-Type", content_type); - req.body = body; - - auto res = std::make_shared(); - - return send(req, *res) ? res : nullptr; -} - -inline std::shared_ptr Client::Patch(const char *path, const std::string &body, const char *content_type) -{ - return Patch(path, Headers(), body, content_type); -} - -inline std::shared_ptr Client::Patch(const char *path, const Headers &headers, const std::string &body, - const char *content_type) -{ - Request req; - req.method = "PATCH"; - req.headers = headers; - req.path = path; - - req.headers.emplace("Content-Type", content_type); - req.body = body; - - auto res = std::make_shared(); - - return send(req, *res) ? res : nullptr; -} - -inline std::shared_ptr Client::Delete(const char *path) -{ - return Delete(path, Headers(), std::string(), nullptr); -} - -inline std::shared_ptr Client::Delete(const char *path, const std::string &body, const char *content_type) -{ - return Delete(path, Headers(), body, content_type); -} - -inline std::shared_ptr Client::Delete(const char *path, const Headers &headers) -{ - return Delete(path, headers, std::string(), nullptr); -} - -inline std::shared_ptr Client::Delete(const char *path, const Headers &headers, const std::string &body, - const char *content_type) -{ - Request req; - req.method = "DELETE"; - req.headers = headers; - req.path = path; - - if(content_type) - { - req.headers.emplace("Content-Type", content_type); - } - req.body = body; - - auto res = std::make_shared(); - - return send(req, *res) ? res : nullptr; -} - -inline std::shared_ptr Client::Options(const char *path) -{ - return Options(path, Headers()); -} - -inline std::shared_ptr Client::Options(const char *path, const Headers &headers) -{ - Request req; - req.method = "OPTIONS"; - req.path = path; - req.headers = headers; - - auto res = std::make_shared(); - - return send(req, *res) ? res : nullptr; -} - -inline void Client::set_keep_alive_max_count(size_t count) -{ - keep_alive_max_count_ = count; -} - -inline void Client::follow_location(bool on) -{ - follow_location_ = on; -} - -/* - * SSL Implementation - */ -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -namespace detail -{ - -template -inline bool process_and_close_socket_ssl(bool is_client_request, socket_t sock, size_t keep_alive_max_count, - SSL_CTX *ctx, std::mutex &ctx_mutex, U SSL_connect_or_accept, V setup, - T callback) -{ - assert(keep_alive_max_count > 0); - - SSL *ssl = nullptr; - { - std::lock_guard guard(ctx_mutex); - ssl = SSL_new(ctx); - } - - if(!ssl) - { - close_socket(sock); - return false; - } - - auto bio = BIO_new_socket(static_cast(sock), BIO_NOCLOSE); - SSL_set_bio(ssl, bio, bio); - - if(!setup(ssl)) - { - SSL_shutdown(ssl); - { - std::lock_guard guard(ctx_mutex); - SSL_free(ssl); - } - - close_socket(sock); - return false; - } - - bool ret = false; - - if(SSL_connect_or_accept(ssl) == 1) - { - if(keep_alive_max_count > 1) - { - auto count = keep_alive_max_count; - while(count > 0 && (is_client_request || detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, - CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) - { - SSLSocketStream strm(sock, ssl); - auto last_connection = count == 1; - auto connection_close = false; - - ret = callback(ssl, strm, last_connection, connection_close); - if(!ret || connection_close) - { - break; - } - - count--; - } - } - else - { - SSLSocketStream strm(sock, ssl); - auto dummy_connection_close = false; - ret = callback(ssl, strm, true, dummy_connection_close); - } - } - - SSL_shutdown(ssl); - { - std::lock_guard guard(ctx_mutex); - SSL_free(ssl); - } - - close_socket(sock); - - return ret; -} - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -static std::shared_ptr> openSSL_locks_; - -class SSLThreadLocks -{ - public: - SSLThreadLocks() - { - openSSL_locks_ = std::make_shared>(CRYPTO_num_locks()); - CRYPTO_set_locking_callback(locking_callback); - } - - ~SSLThreadLocks() - { - CRYPTO_set_locking_callback(nullptr); - } - - private: - static void locking_callback(int mode, int type, const char * /*file*/, int /*line*/) - { - auto &locks = *openSSL_locks_; - if(mode & CRYPTO_LOCK) - { - locks[type].lock(); - } - else - { - locks[type].unlock(); - } - } -}; - -#endif - -class SSLInit -{ - public: - SSLInit() - { - SSL_load_error_strings(); - SSL_library_init(); - } - - ~SSLInit() - { - ERR_free_strings(); - } - - private: -#if OPENSSL_VERSION_NUMBER < 0x10100000L - SSLThreadLocks thread_init_; -#endif -}; - -static SSLInit sslinit_; - -} // namespace detail - -// SSL socket stream implementation -inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl) : sock_(sock), ssl_(ssl) -{ -} - -inline SSLSocketStream::~SSLSocketStream() -{ -} - -inline int SSLSocketStream::read(char *ptr, size_t size) -{ - if(SSL_pending(ssl_) > 0 || - detail::select_read(sock_, CPPHTTPLIB_READ_TIMEOUT_SECOND, CPPHTTPLIB_READ_TIMEOUT_USECOND) > 0) - { - return SSL_read(ssl_, ptr, static_cast(size)); - } - return -1; -} - -inline int SSLSocketStream::write(const char *ptr, size_t size) -{ - return SSL_write(ssl_, ptr, static_cast(size)); -} - -inline int SSLSocketStream::write(const char *ptr) -{ - return write(ptr, strlen(ptr)); -} - -inline int SSLSocketStream::write(const std::string &s) -{ - return write(s.data(), s.size()); -} - -inline std::string SSLSocketStream::get_remote_addr() const -{ - return detail::get_remote_addr(sock_); -} - -// SSL HTTP server implementation -inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, const char *client_ca_cert_file_path, - const char *client_ca_cert_dir_path) -{ - ctx_ = SSL_CTX_new(SSLv23_server_method()); - - if(ctx_) - { - SSL_CTX_set_options(ctx_, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - - // auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); - // SSL_CTX_set_tmp_ecdh(ctx_, ecdh); - // EC_KEY_free(ecdh); - - if(SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 || - SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != 1) - { - SSL_CTX_free(ctx_); - ctx_ = nullptr; - } - else if(client_ca_cert_file_path || client_ca_cert_dir_path) - { - // if (client_ca_cert_file_path) { - // auto list = SSL_load_client_CA_file(client_ca_cert_file_path); - // SSL_CTX_set_client_CA_list(ctx_, list); - // } - - SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path, client_ca_cert_dir_path); - - SSL_CTX_set_verify(ctx_, - SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE, - nullptr); - } - } -} - -inline SSLServer::~SSLServer() -{ - if(ctx_) - { - SSL_CTX_free(ctx_); - } -} - -inline bool SSLServer::is_valid() const -{ - return ctx_; -} - -inline bool SSLServer::process_and_close_socket(socket_t sock) -{ - return detail::process_and_close_socket_ssl( - false, sock, keep_alive_max_count_, ctx_, ctx_mutex_, SSL_accept, [](SSL * /*ssl*/) { return true; }, - [this](SSL *ssl, Stream &strm, bool last_connection, bool &connection_close) { - return process_request(strm, last_connection, connection_close, [&](Request &req) { req.ssl = ssl; }); - }); -} - -// SSL HTTP client implementation -inline SSLClient::SSLClient(const char *host, int port, time_t timeout_sec, const char *client_cert_path, - const char *client_key_path) - : Client(host, port, timeout_sec) -{ - ctx_ = SSL_CTX_new(SSLv23_client_method()); - - detail::split(&host_[0], &host_[host_.size()], '.', - [&](const char *b, const char *e) { host_components_.emplace_back(std::string(b, e)); }); - if(client_cert_path && client_key_path) - { - if(SSL_CTX_use_certificate_file(ctx_, client_cert_path, SSL_FILETYPE_PEM) != 1 || - SSL_CTX_use_PrivateKey_file(ctx_, client_key_path, SSL_FILETYPE_PEM) != 1) - { - SSL_CTX_free(ctx_); - ctx_ = nullptr; - } - } -} - -inline SSLClient::~SSLClient() -{ - if(ctx_) - { - SSL_CTX_free(ctx_); - } -} - -inline bool SSLClient::is_valid() const -{ - return ctx_; -} - -inline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path, const char *ca_cert_dir_path) -{ - if(ca_cert_file_path) - { - ca_cert_file_path_ = ca_cert_file_path; - } - if(ca_cert_dir_path) - { - ca_cert_dir_path_ = ca_cert_dir_path; - } -} - -inline void SSLClient::enable_server_certificate_verification(bool enabled) -{ - server_certificate_verification_ = enabled; -} - -inline long SSLClient::get_openssl_verify_result() const -{ - return verify_result_; -} - -inline SSL_CTX *SSLClient::ssl_context() const noexcept -{ - return ctx_; -} - -inline bool SSLClient::process_and_close_socket( - socket_t sock, size_t request_count, - std::function callback) -{ - - request_count = std::min(request_count, keep_alive_max_count_); - - return is_valid() && detail::process_and_close_socket_ssl( - true, sock, request_count, ctx_, ctx_mutex_, - [&](SSL *ssl) { - if(ca_cert_file_path_.empty()) - { - SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr); - } - else - { - if(!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(), nullptr)) - { - return false; - } - SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr); - } - - if(SSL_connect(ssl) != 1) - { - return false; - } - - if(server_certificate_verification_) - { - verify_result_ = SSL_get_verify_result(ssl); - - if(verify_result_ != X509_V_OK) - { - return false; - } - - auto server_cert = SSL_get_peer_certificate(ssl); - - if(server_cert == nullptr) - { - return false; - } - - if(!verify_host(server_cert)) - { - X509_free(server_cert); - return false; - } - X509_free(server_cert); - } - - return true; - }, - [&](SSL *ssl) { - SSL_set_tlsext_host_name(ssl, host_.c_str()); - return true; - }, - [&](SSL * /*ssl*/, Stream &strm, bool last_connection, bool &connection_close) { - return callback(strm, last_connection, connection_close); - }); -} - -inline bool SSLClient::is_ssl() const -{ - return true; -} - -inline bool SSLClient::verify_host(X509 *server_cert) const -{ - /* Quote from RFC2818 section 3.1 "Server Identity" - - If a subjectAltName extension of type dNSName is present, that MUST - be used as the identity. Otherwise, the (most specific) Common Name - field in the Subject field of the certificate MUST be used. Although - the use of the Common Name is existing practice, it is deprecated and - Certification Authorities are encouraged to use the dNSName instead. - - Matching is performed using the matching rules specified by - [RFC2459]. If more than one identity of a given type is present in - the certificate (e.g., more than one dNSName name, a match in any one - of the set is considered acceptable.) Names may contain the wildcard - character * which is considered to match any single domain name - component or component fragment. E.g., *.a.com matches foo.a.com but - not bar.foo.a.com. f*.com matches foo.com but not bar.com. - - In some cases, the URI is specified as an IP address rather than a - hostname. In this case, the iPAddress subjectAltName must be present - in the certificate and must exactly match the IP in the URI. - - */ - return verify_host_with_subject_alt_name(server_cert) || verify_host_with_common_name(server_cert); -} - -inline bool SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const -{ - auto ret = false; - - auto type = GEN_DNS; - - struct in6_addr addr6; - struct in_addr addr; - size_t addr_len = 0; - -#ifndef __MINGW32__ - if(inet_pton(AF_INET6, host_.c_str(), &addr6)) - { - type = GEN_IPADD; - addr_len = sizeof(struct in6_addr); - } - else if(inet_pton(AF_INET, host_.c_str(), &addr)) - { - type = GEN_IPADD; - addr_len = sizeof(struct in_addr); - } -#endif - - auto alt_names = static_cast( - X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr)); - - if(alt_names) - { - auto dsn_matched = false; - auto ip_mached = false; - - auto count = sk_GENERAL_NAME_num(alt_names); - - for(auto i = 0; i < count && !dsn_matched; i++) - { - auto val = sk_GENERAL_NAME_value(alt_names, i); - if(val->type == type) - { - auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5); - auto name_len = (size_t)ASN1_STRING_length(val->d.ia5); - - if(strlen(name) == name_len) - { - switch(type) - { - case GEN_DNS: - dsn_matched = check_host_name(name, name_len); - break; - - case GEN_IPADD: - if(!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len)) - { - ip_mached = true; - } - break; - } - } - } - } - - if(dsn_matched || ip_mached) - { - ret = true; - } - } - - GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names); - - return ret; -} - -inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const -{ - const auto subject_name = X509_get_subject_name(server_cert); - - if(subject_name != nullptr) - { - char name[BUFSIZ]; - auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, name, sizeof(name)); - - if(name_len != -1) - { - return check_host_name(name, name_len); - } - } - - return false; -} - -inline bool SSLClient::check_host_name(const char *pattern, size_t pattern_len) const -{ - if(host_.size() == pattern_len && host_ == pattern) - { - return true; - } - - // Wildcard match - // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484 - std::vector pattern_components; - detail::split(&pattern[0], &pattern[pattern_len], '.', - [&](const char *b, const char *e) { pattern_components.emplace_back(std::string(b, e)); }); - - if(host_components_.size() != pattern_components.size()) - { - return false; - } - - auto itr = pattern_components.begin(); - for(const auto &h : host_components_) - { - auto &p = *itr; - if(p != h && p != "*") - { - auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' && !p.compare(0, p.size() - 1, h)); - if(!partial_match) - { - return false; - } - } - ++itr; - } - - return true; -} -#endif - -} // namespace httplib - -#endif // CPPHTTPLIB_HTTPLIB_H diff --git a/submodules/cpp-httplib b/submodules/cpp-httplib new file mode 160000 index 0000000..dc6a72a --- /dev/null +++ b/submodules/cpp-httplib @@ -0,0 +1 @@ +Subproject commit dc6a72a0fd2f02c89b7ea900dac050ccfa0136ea diff --git a/submodules/sqlitemoderncpp b/submodules/sqlitemoderncpp new file mode 160000 index 0000000..39d1191 --- /dev/null +++ b/submodules/sqlitemoderncpp @@ -0,0 +1 @@ +Subproject commit 39d119190fbaeb95eb43b6da78176ca25a80f06f