This commit is contained in:
qing committed 2026-07-20 11:12:29 +08:00
commit 70d467d052
97 files changed
+10386

No files matched your search

+74
View File
@@ -0,0 +1,74 @@
#PRJ
*.db
*.crt
*.key
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Linker files
*.ilk
# Debugger Files
*.pdb
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
*.so.*
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# Build directories
build/
Build/
build-*/
# CMake generated files
CMakeFiles/
CMakeCache.txt
cmake_install.cmake
#Makefile
install_manifest.txt
compile_commands.json
# Temporary files
*.tmp
*.log
*.bak
*.swp
# vcpkg
vcpkg_installed/
# debug information files
*.dwo
# test output & cache
Testing/
.cache/
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 dashun
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+19
View File
@@ -0,0 +1,19 @@
VERSION := 0.0.1
APP_NAME := gateway
INSTL_DIR := /usr/local/bin
CPP := g++
INCS := -I inc/
MY_LIBS := -L/usr/lib -lus_http -lus_crypt
LIBS := -lcurl -lssl -lsqlite3 -lz -lcrypto -lboost_json -lboost_program_options
CPPFLAGS := --std=c++20 -pipe # -Wall -Werror
BUILD_DIR = build
$(shell mkdir -p $(BUILD_DIR))
#--------
all: $(BUILD_DIR)/$(APP_NAME).out
$(BUILD_DIR)/$(APP_NAME).out: $(BUILD_DIR)/main.o
$(CPP) $^ $(LIBS) $(MY_LIBS) -o $@
$(BUILD_DIR)/main.o: src/main.cpp
$(CPP) $(CPPFLAGS) -c $^ $(INCS) -o $@
clear:
@rm -rvf build/
+7
View File
@@ -0,0 +1,7 @@
# UniServe_gateway2
US Framework gateway C++ version
# Dependancy
libboost
libcurl
+3
View File
@@ -0,0 +1,3 @@
#!/bin/bash
cd "$(dirname "$0")" || exit
openssl req -x509 -newkey rsa:2048 -nodes -keyout server.key -out server.crt -days 365 -subj "/CN=localhost"
+3
View File
@@ -0,0 +1,3 @@
#!/bin/bash
cd "$(dirname "$0")" || exit
exec ./build/gateway.out -i example.ini #1>/dev/null
+3
View File
@@ -0,0 +1,3 @@
#!/bin/bash
cd "$(dirname "$0")" || exit
make clear && make $@
+9
View File
@@ -0,0 +1,9 @@
[Server]
address = 0.0.0.0
port = 9201
[Proxy]
default = http://127.0.0.1:9200
[Database]
path = sqlite3.db
+42
View File
@@ -0,0 +1,42 @@
#pragma once
#include <iostream>
#include <exception>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
namespace property_tree = boost::property_tree;
using ptree = property_tree::ptree;
namespace qing {
class ConfIni {
public:
ConfIni(const std::string &filename) {
try {
std::cout << "open and parse ini file: " << filename << std::endl;
property_tree::ini_parser::read_ini(filename, pt);
}
catch (const boost::property_tree::ini_parser_error& e) {
std::cerr << "INI parsing error: " << e.what() << std::endl;
throw IniConfigureFileParseException("INI parsing error!");
}
}
template <typename T>
T get(const std::string &path, const T &_defaut) {
return pt.get<T>(path, _defaut);
}
class IniConfigureFileParseException: public std::exception {
public:
explicit IniConfigureFileParseException(const std::string &msg)
: message(msg) {}
const char* what() const noexcept override {
return message.c_str();
}
private:
std::string message;
};
private:
ptree pt;
};
}
+402
View File
@@ -0,0 +1,402 @@
#ifndef EXCEPTION_BASE_H
#define EXCEPTION_BASE_H
#include <exception>
#include <string>
#include <cstring>
#include <memory>
#include <source_location>
#include <sstream>
#include <type_traits>
/**
* @brief 通用异常基类
*
* 特性:
* 1. 完整的错误信息管理(错误码、消息、位置信息)
* 2. 支持异常链(嵌套异常)
* 3. 多态支持(clone、自定义消息)
* 4. 线程安全的 what() 缓存
* 5. 支持 C++20 source_location
*/
class ExceptionBase : public std::exception {
public:
// ============ 构造函数 ============
explicit ExceptionBase(
int code = 0,
const std::string& message = "",
const std::source_location& location = std::source_location::current()
) noexcept
: m_code(code)
, m_message(message)
, m_file(location.file_name())
, m_line(location.line())
, m_function(location.function_name())
, m_what_cache()
{
}
// 支持从字符串字面量构造
explicit ExceptionBase(
const std::string& message,
const std::source_location& location = std::source_location::current()
) noexcept
: m_code(0)
, m_message(message)
, m_file(location.file_name())
, m_line(location.line())
, m_function(location.function_name())
, m_what_cache()
{
}
// ============ 拷贝和移动 ============
ExceptionBase(const ExceptionBase& other) noexcept
: std::exception(other)
, m_code(other.m_code)
, m_message(other.m_message)
, m_file(other.m_file)
, m_line(other.m_line)
, m_function(other.m_function)
, m_nested(other.m_nested ? other.m_nested->clone() : nullptr)
, m_what_cache()
{
}
ExceptionBase(ExceptionBase&& other) noexcept
: std::exception(std::move(other))
, m_code(other.m_code)
, m_message(std::move(other.m_message))
, m_file(std::move(other.m_file))
, m_line(other.m_line)
, m_function(std::move(other.m_function))
, m_nested(std::move(other.m_nested))
, m_what_cache(std::move(other.m_what_cache))
{
other.m_code = 0;
other.m_line = 0;
}
virtual ~ExceptionBase() noexcept = default;
// ============ 赋值运算符 ============
ExceptionBase& operator=(const ExceptionBase& other) noexcept {
if (this != &other) {
m_code = other.m_code;
m_message = other.m_message;
m_file = other.m_file;
m_line = other.m_line;
m_function = other.m_function;
m_nested = other.m_nested ? other.m_nested->clone() : nullptr;
m_what_cache.clear();
}
return *this;
}
ExceptionBase& operator=(ExceptionBase&& other) noexcept {
if (this != &other) {
m_code = other.m_code;
m_message = std::move(other.m_message);
m_file = std::move(other.m_file);
m_line = other.m_line;
m_function = std::move(other.m_function);
m_nested = std::move(other.m_nested);
m_what_cache = std::move(other.m_what_cache);
other.m_code = 0;
other.m_line = 0;
}
return *this;
}
// ============ 访问器 ============
int code() const noexcept { return m_code; }
const std::string& message() const noexcept { return m_message; }
const std::string& file() const noexcept { return m_file; }
int line() const noexcept { return m_line; }
const std::string& function() const noexcept { return m_function; }
const ExceptionBase* nested() const noexcept { return m_nested.get(); }
// ============ 异常链操作 ============
void set_nested(std::unique_ptr<ExceptionBase> nested) noexcept {
m_nested = std::move(nested);
m_what_cache.clear();
}
template<typename ExType>
void set_nested(const ExType& nested_ex) noexcept {
static_assert(std::is_base_of<ExceptionBase, ExType>::value,
"ExType must inherit from ExceptionBase");
m_nested = std::make_unique<ExType>(nested_ex);
m_what_cache.clear();
}
// ============ std::exception 接口 ============
const char* what() const noexcept override {
if (m_what_cache.empty()) {
build_what_message();
}
return m_what_cache.c_str();
}
// ============ 多态支持 ============
virtual std::unique_ptr<ExceptionBase> clone() const {
return std::make_unique<ExceptionBase>(*this);
}
// ============ 重新抛出 ============
[[noreturn]] void rethrow() const {
throw *this;
}
protected:
// ============ 可重写的消息构建方法 ============
virtual std::string build_message() const {
std::string result;
// 错误码
if (m_code != 0) {
result += "[Error " + std::to_string(m_code) + "] ";
}
// 错误消息
result += m_message;
// 位置信息
if (!m_file.empty()) {
result += " (at " + m_file;
if (m_line > 0) {
result += ":" + std::to_string(m_line);
}
if (!m_function.empty()) {
result += " in " + m_function;
}
result += ")";
}
// 嵌套异常
if (m_nested) {
result += "\n Caused by: " + std::string(m_nested->what());
}
return result;
}
private:
// ============ 内部方法 ============
void build_what_message() const noexcept {
try {
m_what_cache = build_message();
} catch (...) {
// 降级处理
m_what_cache = "Exception: " + m_message;
}
}
private:
// ============ 成员变量 ============
int m_code;
std::string m_message;
std::string m_file;
int m_line;
std::string m_function;
std::unique_ptr<ExceptionBase> m_nested;
mutable std::string m_what_cache;
};
// ============================================================================
// 宏定义 - 方案二完整版
// ============================================================================
/**
* @brief 抛出指定类型的异常(自动添加 source_location)
*
* 适用场景:子类构造函数与 ExceptionBase 相同
*
* 使用示例:
* THROW_EX(NetworkException, 404, "Server not found")
* THROW_EX(DatabaseException, 100, "Connection timeout")
*/
#define THROW_EX(ExType, code, msg) \
throw ExType(code, msg, std::source_location::current())
/**
* @brief 抛出异常(仅消息,不传错误码)
*
* 适用场景:子类支持只传消息的构造函数
*
* 使用示例:
* THROW_EX_MSG(ValidationException, "Invalid input data")
*/
#define THROW_EX_MSG(ExType, msg) \
throw ExType(msg, std::source_location::current())
/**
* @brief 抛出异常(支持任意构造函数参数)
*
* 适用场景:子类有额外的构造函数参数
*
* 使用示例:
* // 子类定义
* class FileException : public ExceptionBase {
* public:
* FileException(int code, const std::string& msg,
* const std::string& filename,
* const std::source_location& loc = std::source_location::current())
* : ExceptionBase(code, msg, loc), m_filename(filename) {}
* };
*
* // 使用
* THROW_EX_CUSTOM(FileException, 404, "File not found", "/etc/config.txt")
*/
#define THROW_EX_CUSTOM(ExType, ...) \
throw ExType(__VA_ARGS__, std::source_location::current())
/**
* @brief 抛出异常并携带嵌套异常
*
* 适用场景:需要保留原始异常信息
*
* 使用示例:
* try {
* connect_to_server();
* } catch (const NetworkException& e) {
* THROW_EX_NESTED(ServiceException, 500, "Service failed", e)
* }
*/
#define THROW_EX_NESTED(ExType, code, msg, nested_ex) \
do { \
auto ex = ExType(code, msg, std::source_location::current()); \
ex.set_nested(nested_ex); \
throw ex; \
} while(0)
/**
* @brief 抛出异常并携带嵌套异常(支持自定义参数)
*
* 使用示例:
* THROW_EX_NESTED_CUSTOM(FileException, 500, "Read failed",
* original_ex, "/data/file.txt")
*/
#define THROW_EX_NESTED_CUSTOM(ExType, code, msg, nested_ex, ...) \
do { \
auto ex = ExType(code, msg, __VA_ARGS__, std::source_location::current()); \
ex.set_nested(nested_ex); \
throw ex; \
} while(0)
/**
* @brief 检查条件,失败则抛出异常
*
* 使用示例:
* THROW_IF(ptr == nullptr, NullPointerException, 100, "Pointer is null")
* THROW_IF(data.empty(), ValidationException, 200, "Data cannot be empty")
*/
#define THROW_IF(cond, ExType, code, msg) \
do { \
if (cond) { \
THROW_EX(ExType, code, msg); \
} \
} while(0)
/**
* @brief 检查条件,失败则抛出异常(仅消息)
*/
#define THROW_IF_MSG(cond, ExType, msg) \
do { \
if (cond) { \
THROW_EX_MSG(ExType, msg); \
} \
} while(0)
/**
* @brief 检查条件,失败则抛出异常(自定义参数)
*/
#define THROW_IF_CUSTOM(cond, ExType, ...) \
do { \
if (cond) { \
THROW_EX_CUSTOM(ExType, __VA_ARGS__); \
} \
} while(0)
/**
* @brief 检查条件,失败则抛出异常(带嵌套异常)
*/
#define THROW_IF_NESTED(cond, ExType, code, msg, nested_ex) \
do { \
if (cond) { \
THROW_EX_NESTED(ExType, code, msg, nested_ex); \
} \
} while(0)
/**
* @brief 检查条件,失败则抛出异常(带嵌套异常和自定义参数)
*/
#define THROW_IF_NESTED_CUSTOM(cond, ExType, code, msg, nested_ex, ...) \
do { \
if (cond) { \
THROW_EX_NESTED_CUSTOM(ExType, code, msg, nested_ex, __VA_ARGS__); \
} \
} while(0)
// ============================================================================
// 辅助宏 - 用于格式化错误消息
// ============================================================================
/**
* @brief 格式化错误消息(使用流式语法)
*
* 使用示例:
* THROW_EX_FMT(NetworkException, 404, "Failed to connect to " << host << ":" << port)
*/
#define THROW_EX_FMT(ExType, code, msg_expr) \
do { \
std::ostringstream _oss; \
_oss << msg_expr; \
THROW_EX(ExType, code, _oss.str()); \
} while(0)
/**
* @brief 格式化错误消息(使用 printf 风格)
*
* 使用示例:
* THROW_EX_PRINTF(NetworkException, 404, "Failed to connect to %s:%d", host.c_str(), port)
*/
#define THROW_EX_PRINTF(ExType, code, fmt, ...) \
do { \
char _buf[1024]; \
snprintf(_buf, sizeof(_buf), fmt, __VA_ARGS__); \
THROW_EX(ExType, code, std::string(_buf)); \
} while(0)
/**
* @brief 条件检查 + 格式化消息
*/
#define THROW_IF_FMT(cond, ExType, code, msg_expr) \
do { \
if (cond) { \
THROW_EX_FMT(ExType, code, msg_expr); \
} \
} while(0)
/**
* @brief 条件检查 + printf 风格格式化
*/
#define THROW_IF_PRINTF(cond, ExType, code, fmt, ...) \
do { \
if (cond) { \
THROW_EX_PRINTF(ExType, code, fmt, __VA_ARGS__); \
} \
} while(0)
#endif // EXCEPTION_BASE_H
+523
View File
@@ -0,0 +1,523 @@
#include <us/HttpServer.hpp>
#include <us/HttpClient.hpp>
#include <us/MyRSA.hpp>
#include <us/Base64.hpp>
#include <us/SHA256.hpp>
#include <us/Sqlite3.hpp>
#include <iostream>
#include <typeinfo>
#include <memory>
#include <csignal>
#include <boost/json.hpp>
#include <boost/program_options.hpp>
#include <openssl/provider.h>
#include "ExpBase.hpp"
#include "Conf.hpp"
using namespace pmc::net;
using namespace qing;
namespace json = boost::json;
namespace po = boost::program_options;
constexpr const char *_VER = "0.2.1";
class DatabaseException: public ExceptionBase {
public:
using ExceptionBase::ExceptionBase;
std::string build_message() const override {
return "[Database]" + ExceptionBase::build_message();
}
};
class HttpException: public ExceptionBase {
public:
HttpException(int code, const std::string& msg,
int http_status, const std::string& url,
const std::source_location& loc = std::source_location::current())
: ExceptionBase(code, msg, loc)
, m_http_status(http_status)
, m_url(url) {}
std::string build_message() const override {
return "[Http]" + ExceptionBase::build_message();
}
int status() const {
return m_http_status;
}
const std::string& url() const {
return m_url;
}
private:
int m_http_status;
std::string m_url;
};
void db_init();
void registerRoutes(HttpServer &server);
http::response<http::string_body> wrapperHttp(
const http::request<http::string_body>& req,
const std::unordered_map<std::string, std::string>& params,
HttpRequestHandler _main);
http::response<http::string_body> handleProxy(
const http::request<http::string_body>& req,
const std::unordered_map<std::string, std::string>& params,
const char *method
);
http::response<http::string_body> handleLogin(
const http::request<http::string_body>& req,
const std::unordered_map<std::string, std::string>& params);
http::response<http::string_body> handleRegister(
const http::request<http::string_body>& req,
const std::unordered_map<std::string, std::string>& params);
/* headers can't processed */
bool is_special_header(const std::string &header);
/* configure items */
static std::string _ADDR;
static int _PORT;
static std::string _DEFT_URL;
static std::string _DB_PATH;
static std::string _DEFT_ADMIN_USER = "admin";
static std::string _DEFT_ADMIN_PASS = "44112233";
/* instance items */
static std::unique_ptr<HttpServer> _SERVE;
static std::unique_ptr<MyRSA::Generator> _KEYGEN;
static std::unique_ptr<Sqlite3> _SQLDB;
void signalHandler(int signum) {
std::cout << "Receive close signal, prepare to shutdown...\n";
if (_SERVE) {
_SERVE->stop();
}
}
void set_signal_action() {
struct sigaction sa;
sa.sa_handler = signalHandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINT, &sa, nullptr);
sigaction(SIGTERM, &sa, nullptr);
}
int main(int argc, char **argv) {
po::options_description desc("cpp language web gateway");
desc.add_options()
("help,h", "output help message")
("version,v", "output version message")
("input,i", po::value<std::string>(), "input a configure text file")
;
po::variables_map vm;
/* parse commandline params */
{
try {
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
}
catch (const std::exception &e) {
std::cerr << "ERROR: " << e.what() << std::endl;
std::cerr << "using --help to check options message" << std::endl;
throw e;
}
}
if (vm.count("help")) {
std::cout << desc << std::endl;
return 0;
}
if (!vm.count("input")) {
std::cerr << "Please input configure file via -i" << std::endl;
return 0;
}
/* parse configure file */
{
try {
ConfIni cf(vm["input"].as<std::string>());
_ADDR = cf.get<std::string>("Server.address", "");
_PORT = cf.get<int> ("Server.port", -1);
_DEFT_URL = cf.get<std::string>("Proxy.default", "");
_DB_PATH = cf.get<std::string>("Database.path", "");
}
catch (ConfIni::IniConfigureFileParseException &e) {
std::cerr << "ERROR: " << e.what() << std::endl;
std::cerr << "ini format input file error" << std::endl;
return 1;
}
}
if (_ADDR == "" || _PORT == -1) {
std::cerr << "Please setup addree and port in Serve section in configure file";
std::cerr << std::endl;
return 0;
}
_SERVE = std::make_unique<HttpServer> (_ADDR, _PORT, "full_chain.crt", "server.key", 4);
_KEYGEN = std::make_unique<MyRSA::Generator> ();
_SQLDB = std::make_unique<Sqlite3> (_DB_PATH);
db_init();
registerRoutes(*_SERVE);
set_signal_action();
_SERVE->start();
_SERVE->run();
}
void db_init() {
std::string sql = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='Users'";
auto flag = false;
{
auto stmt = _SQLDB->stmt(sql);
if (stmt.step()) {
auto num = stmt.read_int(0);
if (num == 0) {
flag = true;
}
}
}
if (flag) {
std::cout << "Start database init..." << std::endl;
std::string sql_crtt = "-- create users table, username as email\n"
"CREATE TABLE Users (\n"
"id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
"email TEXT UNIQUE NOT NULL,\n"
"password_hash TEXT NOT NULL,\n"
"create_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n"
"last_login DATETIME,\n"
"is_active BOOLEAN DEFAULT 1\n"
");\n\n"
"CREATE INDEX idx_users_email ON users(email);\n";
_SQLDB->exec(sql_crtt);
{
auto password_hash = SHA256::sha256(_DEFT_ADMIN_PASS);
std::cout << password_hash << std::endl;
std::string sql_crta = "INSERT INTO Users(email, password_hash, last_login) VALUES (?, ?, datetime('now', 'localtime'))";
auto stmt = _SQLDB->stmt(sql_crta);
stmt.bind_text(1, _DEFT_ADMIN_USER);
stmt.bind_text(2, password_hash);
if (stmt.step()) {
std::cout << stmt.read_text(1) << std::endl;
}
}
std::cout << "done" << std::endl;
}
else {
std::cout << "The database already init" << std::endl;
}
}
void registerRoutes(HttpServer &server) {
server.post("/login", [](
const http::request<http::string_body>& req,
const std::unordered_map<std::string, std::string>& params
) -> http::response<http::string_body> {
return wrapperHttp(req, params, [](
const auto& req, const auto& params
) {
return handleLogin(req, params);
});
});
server.post("/register", [](const auto& req, const auto& params) {
return handleRegister(req, params);
});
server.get("*", [](const auto& req, const auto& params) {
return handleProxy(req, params, "get");
});
server.post("*", [](const auto& req, const auto& params) {
return handleProxy(req, params, "post");
});
}
http::response<http::string_body> wrapperHttp(
const http::request<http::string_body>& req,
const std::unordered_map<std::string, std::string>& params,
HttpRequestHandler _main)
try {
return _main(req, params);
}
catch (const std::exception& e) {
http::response<http::string_body> res;
res.version(req.version());
const auto& ti = typeid(e);
json::value jv_r = {
{"success", false},
{"message", std::string("<") + ti.name() + "> " + e.what()}
};
res.set(http::field::content_type, "text/plain");
res.result(http::status::internal_server_error);
res.body() = json::serialize(jv_r);
res.prepare_payload();
return res;
}
/* curl -X POST http://127.0.0.1:9201/login -d '{"username": "qing", "password": "12345678"}' */
http::response<http::string_body> handleLogin(
const http::request<http::string_body>& req,
const std::unordered_map<std::string, std::string>& params)
{
http::response<http::string_body> res;
res.version(req.version());
try {
//std::cout << req.body() << std::endl;
json::value jv;
try {
jv = json::parse(req.body());
}
catch (const boost::wrapexcept<boost::system::system_error>& e) {
THROW_EX_CUSTOM(HttpException, 2001, "Invalid_json_Argument",
400, "/login");
}
if (!jv.is_object()) {
THROW_EX_CUSTOM(HttpException, 2002, "Invalid_json_object_Argument",
400, "/login");
}
auto& obj = jv.as_object();
auto username = std::string(obj.at("username").as_string());
auto password = std::string(obj.at("password").as_string());
auto password_hash = SHA256::sha256(password);
std::cout << username << " " << password_hash << std::endl;
std::string sql = "SELECT COUNT(*) FROM Users WHERE email=? AND password_hash=?;";
auto stmt = _SQLDB->stmt(sql);
stmt.bind_text(1, username);
stmt.bind_text(2, password_hash);
if (stmt.step() && stmt.read_int(0) == 1) {
//update
}
else {
THROW_EX_CUSTOM(HttpException, 2101, "Invalid Username or Password",
401, "/login");
}
auto _PRIV = MyRSA::Private_Key::from_pem(_KEYGEN->get_private_key_pem());
//int created_at = 0;
int keep_time = 3600; // second
json::value jv_r = {
{"user", username},
//{"created_at", created_at},
{"keep_time", keep_time}
};
std::string data = json::serialize(jv_r);
std::string token = Base64::base64_encode(data);
std::string sign = _PRIV.Sign(data);
res.set("Connection", "close");
res.result(http::status::ok);
res.body() = token + "_" + sign;
}
catch (const HttpException& e) {
json::value jv_r = {
{"success", false},
{"message", e.what()}
};
res.set(http::field::content_type, "application/json");
res.result(e.status());
res.body() = json::serialize(jv_r);
}
res.prepare_payload();
return res;
}
/* curl -X POST http://127.0.0.1:9201/register -d '{"username": "qing", "password": "12345678"}' */
/* FIXME: strong password check */
http::response<http::string_body> handleRegister(
const http::request<http::string_body>& req,
const std::unordered_map<std::string, std::string>& params)
{
http::response<http::string_body> res;
res.version(req.version());
try {
auto jv = json::parse(req.body());
if (!jv.is_object()) {
throw std::runtime_error("400");
}
auto& obj = jv.as_object();
auto username = std::string(obj.at("username").as_string());
auto password = std::string(obj.at("password").as_string());
auto password_hash = SHA256::sha256(password);
std::string sql = "INSERT INTO Users (email, password_hash, last_login) VALUES (?, ?, datetime('now', 'localtime'));";
auto stmt = _SQLDB->stmt(sql);
stmt.bind_text(1, username);
stmt.bind_text(2, password_hash);
stmt.step();
json::value jv_r = {{"success", true}};
res.result(http::status::ok);
res.body() = json::serialize(jv_r);
}
//catch
catch (const std::exception& e) {
const auto& ti = typeid(e);
res.set(http::field::content_type, "text/plain");
res.result(http::status::internal_server_error);
res.body() = std::string("<") + ti.name() + "> " + e.what();
}
res.prepare_payload();
return res;
}
/* curl -H "Target-Url: https://www.baidu.com" http://127.0.0.1:9201 -X GET */
http::response<http::string_body> handleProxy(
const http::request<http::string_body>& req,
const std::unordered_map<std::string, std::string>& params,
const char *method)
{
http::response<http::string_body> res;
res.version(req.version());
try {
auto _PUBL = MyRSA::Public_Key::from_pem(_KEYGEN->get_public_key_pem());
std::string url = "";
auto flag = false;
{
auto it = req.base().find("Target-Url");
if (it != req.base().end()) {
url = req["Target-Url"];
}
it = req.base().find("target-url");
if (it != req.base().end()) {
url = req["target-url"];
}
if (url == "") {
url = _DEFT_URL;
flag = true;
}
url += "/";
url += req.target();
}
/* check auth */
std::string auth = "";
{
auto it = req.base().find("Authorization");
if (it == req.base().end() && !flag) {
throw std::runtime_error("401");
}
std::string raw = req["Authorization"];
if (raw.substr(0, 7) == "Bearer ") {
raw = raw.substr(7, raw.size() - 7);
size_t idx = raw.find("_");
if (idx == std::string::npos) {
throw std::runtime_error("Header 400 Bad Request");
}
std::string data = raw.substr(0, idx);
std::string sign = raw.substr(idx+1, raw.size()-idx-1);
auth = Base64::base64_decode_to_string(data);
if (!_PUBL.Verify(auth, sign)) {
throw std::runtime_error("Header 401 Unauthorized");
}
std::cout << "auth data: " << auth << std::endl;
}
}
auto callback = [&res](const auto& ret) {
res.body() += ret; // TODO: too big file
};
auto client = HttpClient();
for (auto const& field: req) {
if (is_special_header(field.name_string()))
continue;
auto h = std::string();
h += field.name_string();
h += ": ";
h += field.value();
client.set_header(h);
}
if (method == "get")
client.get_stream_sync(url, callback);
else if (method == "post") {
std::string payload = req.body();
client.post_stream_sync(url, payload, callback);
}
for (const auto& pair: client.get_response_headers() ) {
res.set(pair.first, pair.second);
}
res.set("Connection", "close");
res.result(client.get_response_code());
}
//catch
catch (const std::exception& e) {
const auto& ti = typeid(e);
res.set(http::field::content_type, "text/plain");
res.result(http::status::internal_server_error);
json::value jv_r = {
{"success", false},
{"err_name", ti.name()},
{"err_message", e.what()}
};
res.body() = json::serialize(jv_r);
}
res.prepare_payload();
return res;
}
/* they are special because they point to raw request */
bool is_special_header(const std::string &header) {
return header == "Host" || header == "connection"
|| header == "Accept-Encoding";
}