initial commit

This commit is contained in:
2025-08-05 15:53:44 +08:00
commit 09dc02ae52
553 changed files with 137665 additions and 0 deletions

370
third_party/libhv/examples/httpd/handler.cpp vendored Executable file
View File

@@ -0,0 +1,370 @@
#include "handler.h"
#include <thread> // import std::thread
#include <chrono> // import std::chrono
#include "hbase.h"
#include "htime.h"
#include "hfile.h"
#include "hstring.h"
#include "EventLoop.h" // import setTimeout, setInterval
int Handler::preprocessor(HttpRequest* req, HttpResponse* resp) {
// printf("%s:%d\n", req->client_addr.ip.c_str(), req->client_addr.port);
// printf("%s\n", req->Dump(true, true).c_str());
#if REDIRECT_HTTP_TO_HTTPS
// 301
if (req->scheme == "http") {
std::string location = hv::asprintf("https://%s:%d%s", req->host.c_str(), 8443, req->path.c_str());
return resp->Redirect(location, HTTP_STATUS_MOVED_PERMANENTLY);
}
#endif
// Unified verification request Content-Type?
// if (req->content_type != APPLICATION_JSON) {
// return response_status(resp, HTTP_STATUS_BAD_REQUEST);
// }
// Deserialize request body to json, form, etc.
req->ParseBody();
// Unified setting response Content-Type?
resp->content_type = APPLICATION_JSON;
return HTTP_STATUS_NEXT;
}
int Handler::postprocessor(HttpRequest* req, HttpResponse* resp) {
// printf("%s\n", resp->Dump(true, true).c_str());
return resp->status_code;
}
int Handler::errorHandler(const HttpContextPtr& ctx) {
int error_code = ctx->response->status_code;
return response_status(ctx, error_code);
}
int Handler::Authorization(HttpRequest* req, HttpResponse* resp) {
// authentication sample code
if (strcmp(req->path.c_str(), "/login") == 0) {
return HTTP_STATUS_NEXT;
}
std::string token = req->GetHeader("Authorization");
if (token.empty()) {
response_status(resp, 10011, "Miss Authorization header!");
return HTTP_STATUS_UNAUTHORIZED;
}
else if (strcmp(token.c_str(), "abcdefg") != 0) {
response_status(resp, 10012, "Authorization failed!");
return HTTP_STATUS_UNAUTHORIZED;
}
return HTTP_STATUS_NEXT;
}
int Handler::sleep(const HttpRequestPtr& req, const HttpResponseWriterPtr& writer) {
writer->WriteHeader("X-Response-tid", hv_gettid());
unsigned long long start_ms = gettimeofday_ms();
writer->response->Set("start_ms", start_ms);
std::string strTime = req->GetParam("t", "1000");
if (!strTime.empty()) {
int ms = atoi(strTime.c_str());
if (ms > 0) {
hv_delay(ms);
}
}
unsigned long long end_ms = gettimeofday_ms();
writer->response->Set("end_ms", end_ms);
writer->response->Set("cost_ms", end_ms - start_ms);
response_status(writer, 0, "OK");
return 200;
}
int Handler::setTimeout(const HttpContextPtr& ctx) {
unsigned long long start_ms = gettimeofday_ms();
ctx->set("start_ms", start_ms);
std::string strTime = ctx->param("t", "1000");
if (!strTime.empty()) {
int ms = atoi(strTime.c_str());
if (ms > 0) {
hv::setTimeout(ms, [ctx, start_ms](hv::TimerID timerID){
unsigned long long end_ms = gettimeofday_ms();
ctx->set("end_ms", end_ms);
ctx->set("cost_ms", end_ms - start_ms);
response_status(ctx, 0, "OK");
});
}
}
return HTTP_STATUS_UNFINISHED;
}
int Handler::query(const HttpContextPtr& ctx) {
// scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
// ?query => HttpRequest::query_params
for (auto& param : ctx->params()) {
ctx->set(param.first.c_str(), param.second);
}
response_status(ctx, 0, "OK");
return 200;
}
int Handler::kv(HttpRequest* req, HttpResponse* resp) {
if (req->content_type != APPLICATION_URLENCODED) {
return response_status(resp, HTTP_STATUS_BAD_REQUEST);
}
resp->content_type = APPLICATION_URLENCODED;
resp->kv = req->GetUrlEncoded();
resp->SetUrlEncoded("int", 123);
resp->SetUrlEncoded("float", 3.14);
resp->SetUrlEncoded("string", "hello");
return 200;
}
int Handler::json(HttpRequest* req, HttpResponse* resp) {
if (req->content_type != APPLICATION_JSON) {
return response_status(resp, HTTP_STATUS_BAD_REQUEST);
}
resp->content_type = APPLICATION_JSON;
resp->json = req->GetJson();
resp->json["int"] = 123;
resp->json["float"] = 3.14;
resp->json["string"] = "hello";
return 200;
}
int Handler::form(HttpRequest* req, HttpResponse* resp) {
if (req->content_type != MULTIPART_FORM_DATA) {
return response_status(resp, HTTP_STATUS_BAD_REQUEST);
}
resp->content_type = MULTIPART_FORM_DATA;
resp->form = req->GetForm();
resp->SetFormData("int", 123);
resp->SetFormData("float", 3.14);
resp->SetFormData("string", "hello");
// resp->SetFormFile("file", "test.jpg");
return 200;
}
int Handler::grpc(HttpRequest* req, HttpResponse* resp) {
if (req->content_type != APPLICATION_GRPC) {
return response_status(resp, HTTP_STATUS_BAD_REQUEST);
}
// parse protobuf
// ParseFromString(req->body);
// resp->content_type = APPLICATION_GRPC;
// serailize protobuf
// resp->body = SerializeAsString(xxx);
response_status(resp, 0, "OK");
return 200;
}
int Handler::test(const HttpContextPtr& ctx) {
ctx->setContentType(ctx->type());
ctx->set("bool", ctx->get<bool>("bool"));
ctx->set("int", ctx->get<int>("int"));
ctx->set("float", ctx->get<float>("float"));
ctx->set("string", ctx->get("string"));
response_status(ctx, 0, "OK");
return 200;
}
int Handler::restful(const HttpContextPtr& ctx) {
// RESTful /:field/ => HttpRequest::query_params
// path=/group/:group_name/user/:user_id
std::string group_name = ctx->param("group_name");
std::string user_id = ctx->param("user_id");
ctx->set("group_name", group_name);
ctx->set("user_id", user_id);
response_status(ctx, 0, "OK");
return 200;
}
int Handler::login(const HttpContextPtr& ctx) {
std::string username = ctx->get("username");
std::string password = ctx->get("password");
if (username.empty() || password.empty()) {
response_status(ctx, 10001, "Miss username or password");
return HTTP_STATUS_BAD_REQUEST;
}
else if (strcmp(username.c_str(), "admin") != 0) {
response_status(ctx, 10002, "Username not exist");
return HTTP_STATUS_BAD_REQUEST;
}
else if (strcmp(password.c_str(), "123456") != 0) {
response_status(ctx, 10003, "Password wrong");
return HTTP_STATUS_BAD_REQUEST;
}
else {
ctx->set("token", "abcdefg");
response_status(ctx, 0, "OK");
return HTTP_STATUS_OK;
}
}
int Handler::upload(const HttpContextPtr& ctx) {
int status_code = 200;
std::string save_path = "html/uploads/";
if (ctx->is(MULTIPART_FORM_DATA)) {
status_code = ctx->request->SaveFormFile("file", save_path.c_str());
} else {
std::string filename = ctx->param("filename", "unnamed.txt");
std::string filepath = save_path + filename;
status_code = ctx->request->SaveFile(filepath.c_str());
}
return response_status(ctx, status_code);
}
int Handler::recvLargeFile(const HttpContextPtr& ctx, http_parser_state state, const char* data, size_t size) {
// printf("recvLargeFile state=%d\n", (int)state);
int status_code = HTTP_STATUS_UNFINISHED;
HFile* file = (HFile*)ctx->userdata;
switch (state) {
case HP_HEADERS_COMPLETE:
{
if (ctx->is(MULTIPART_FORM_DATA)) {
// NOTE: You can use multipart_parser if you want to use multipart/form-data.
ctx->close();
return HTTP_STATUS_BAD_REQUEST;
}
std::string save_path = "html/uploads/";
std::string filename = ctx->param("filename", "unnamed.txt");
std::string filepath = save_path + filename;
file = new HFile;
if (file->open(filepath.c_str(), "wb") != 0) {
ctx->close();
return HTTP_STATUS_INTERNAL_SERVER_ERROR;
}
ctx->userdata = file;
}
break;
case HP_BODY:
{
if (file && data && size) {
if (file->write(data, size) != size) {
ctx->close();
return HTTP_STATUS_INTERNAL_SERVER_ERROR;
}
}
}
break;
case HP_MESSAGE_COMPLETE:
{
status_code = HTTP_STATUS_OK;
ctx->setContentType(APPLICATION_JSON);
response_status(ctx, status_code);
if (file) {
delete file;
ctx->userdata = NULL;
}
}
break;
case HP_ERROR:
{
if (file) {
file->remove();
delete file;
ctx->userdata = NULL;
}
}
break;
default:
break;
}
return status_code;
}
int Handler::sendLargeFile(const HttpContextPtr& ctx) {
std::thread([ctx](){
ctx->writer->Begin();
std::string filepath = ctx->service->document_root + ctx->request->Path();
HFile file;
if (file.open(filepath.c_str(), "rb") != 0) {
ctx->writer->WriteStatus(HTTP_STATUS_NOT_FOUND);
ctx->writer->WriteHeader("Content-Type", "text/html");
ctx->writer->WriteBody("<center><h1>404 Not Found</h1></center>");
ctx->writer->End();
return;
}
http_content_type content_type = CONTENT_TYPE_NONE;
const char* suffix = hv_suffixname(filepath.c_str());
if (suffix) {
content_type = http_content_type_enum_by_suffix(suffix);
}
if (content_type == CONTENT_TYPE_NONE || content_type == CONTENT_TYPE_UNDEFINED) {
content_type = APPLICATION_OCTET_STREAM;
}
size_t filesize = file.size();
ctx->writer->WriteHeader("Content-Type", http_content_type_str(content_type));
#if USE_TRANSFER_ENCODING_CHUNKED
ctx->writer->WriteHeader("Transfer-Encoding", "chunked");
#else
ctx->writer->WriteHeader("Content-Length", filesize);
#endif
ctx->writer->EndHeaders();
char* buf = NULL;
int len = 40960; // 40K
SAFE_ALLOC(buf, len);
size_t total_readbytes = 0;
int last_progress = 0;
int sleep_ms_per_send = 0;
if (ctx->service->limit_rate <= 0) {
// unlimited
} else {
sleep_ms_per_send = len * 1000 / 1024 / ctx->service->limit_rate;
}
if (sleep_ms_per_send == 0) sleep_ms_per_send = 1;
int sleep_ms = sleep_ms_per_send;
auto start_time = std::chrono::steady_clock::now();
auto end_time = start_time;
while (total_readbytes < filesize) {
if (!ctx->writer->isConnected()) {
break;
}
if (!ctx->writer->isWriteComplete()) {
hv_delay(1);
continue;
}
size_t readbytes = file.read(buf, len);
if (readbytes <= 0) {
// read file error!
ctx->writer->close();
break;
}
int nwrite = ctx->writer->WriteBody(buf, readbytes);
if (nwrite < 0) {
// disconnected!
break;
}
total_readbytes += readbytes;
int cur_progress = total_readbytes * 100 / filesize;
if (cur_progress > last_progress) {
// printf("<< %s progress: %ld/%ld = %d%%\n",
// ctx->request->path.c_str(), (long)total_readbytes, (long)filesize, (int)cur_progress);
last_progress = cur_progress;
}
end_time += std::chrono::milliseconds(sleep_ms);
std::this_thread::sleep_until(end_time);
}
ctx->writer->End();
SAFE_FREE(buf);
// auto elapsed_time = std::chrono::duration_cast<std::chrono::seconds>(end_time - start_time);
// printf("<< %s taked %ds\n", ctx->request->path.c_str(), (int)elapsed_time.count());
}).detach();
return HTTP_STATUS_UNFINISHED;
}
int Handler::sse(const HttpContextPtr& ctx) {
// SSEvent(message) every 1s
hv::setInterval(1000, [ctx](hv::TimerID timerID) {
if (ctx->writer->isConnected()) {
char szTime[DATETIME_FMT_BUFLEN] = {0};
datetime_t now = datetime_now();
datetime_fmt(&now, szTime);
ctx->writer->SSEvent(szTime);
} else {
hv::killTimer(timerID);
}
});
return HTTP_STATUS_UNFINISHED;
}

56
third_party/libhv/examples/httpd/handler.h vendored Executable file
View File

@@ -0,0 +1,56 @@
#ifndef HV_HTTPD_HANDLER_H
#define HV_HTTPD_HANDLER_H
#include "HttpService.h"
class Handler {
public:
// preprocessor => middleware -> handlers => postprocessor
static int preprocessor(HttpRequest* req, HttpResponse* resp);
static int postprocessor(HttpRequest* req, HttpResponse* resp);
static int errorHandler(const HttpContextPtr& ctx);
// middleware
static int Authorization(HttpRequest* req, HttpResponse* resp);
static int sleep(const HttpRequestPtr& req, const HttpResponseWriterPtr& writer);
static int setTimeout(const HttpContextPtr& ctx);
static int query(const HttpContextPtr& ctx);
static int kv(HttpRequest* req, HttpResponse* resp);
static int json(HttpRequest* req, HttpResponse* resp);
static int form(HttpRequest* req, HttpResponse* resp);
static int grpc(HttpRequest* req, HttpResponse* resp);
static int test(const HttpContextPtr& ctx);
static int restful(const HttpContextPtr& ctx);
static int login(const HttpContextPtr& ctx);
static int upload(const HttpContextPtr& ctx);
// SSE: Server Send Events
static int sse(const HttpContextPtr& ctx);
// LargeFile
static int sendLargeFile(const HttpContextPtr& ctx);
static int recvLargeFile(const HttpContextPtr& ctx, http_parser_state state, const char* data, size_t size);
private:
static int response_status(HttpResponse* resp, int code = 200, const char* message = NULL) {
if (message == NULL) message = http_status_str((enum http_status)code);
resp->Set("code", code);
resp->Set("message", message);
return code;
}
static int response_status(const HttpResponseWriterPtr& writer, int code = 200, const char* message = NULL) {
response_status(writer->response.get(), code, message);
writer->End();
return code;
}
static int response_status(const HttpContextPtr& ctx, int code = 200, const char* message = NULL) {
response_status(ctx->response.get(), code, message);
ctx->send();
return code;
}
};
#endif // HV_HTTPD_HANDLER_H

340
third_party/libhv/examples/httpd/httpd.cpp vendored Executable file
View File

@@ -0,0 +1,340 @@
#include "hv.h"
#include "hssl.h"
#include "hmain.h"
#include "iniparser.h"
#include "HttpServer.h"
#include "hasync.h" // import hv::async
#include "router.h"
hv::HttpServer g_http_server;
hv::HttpService g_http_service;
static void print_version();
static void print_help();
static int parse_confile(const char* confile);
// long options
static const option_t long_options[] = {
{'h', "help", NO_ARGUMENT, "Print this information"},
{'v', "version", NO_ARGUMENT, "Print version"},
{'c', "confile", REQUIRED_ARGUMENT, "Set configure file, default etc/{program}.conf"},
{'t', "test", NO_ARGUMENT, "Test configure file and exit"},
{'s', "signal", REQUIRED_ARGUMENT, "send signal to process, signal=[start,stop,restart,status,reload]"},
{'d', "daemon", NO_ARGUMENT, "Daemonize"},
{'p', "port", REQUIRED_ARGUMENT, "Set listen port"}
};
void print_version() {
printf("%s version %s\n", g_main_ctx.program_name, hv_compile_version());
}
void print_help() {
char detail_options[1024] = {0};
dump_opt_long(long_options, ARRAY_SIZE(long_options), detail_options, sizeof(detail_options));
printf("%s\n", detail_options);
}
int parse_confile(const char* confile) {
IniParser ini;
int ret = ini.LoadFromFile(confile);
if (ret != 0) {
printf("Load confile [%s] failed: %d\n", confile, ret);
exit(-40);
}
// logfile
std::string str = ini.GetValue("logfile");
if (!str.empty()) {
strncpy(g_main_ctx.logfile, str.c_str(), sizeof(g_main_ctx.logfile));
}
hlog_set_file(g_main_ctx.logfile);
// loglevel
str = ini.GetValue("loglevel");
if (!str.empty()) {
hlog_set_level_by_str(str.c_str());
}
// log_filesize
str = ini.GetValue("log_filesize");
if (!str.empty()) {
hlog_set_max_filesize_by_str(str.c_str());
}
// log_remain_days
str = ini.GetValue("log_remain_days");
if (!str.empty()) {
hlog_set_remain_days(atoi(str.c_str()));
}
// log_fsync
str = ini.GetValue("log_fsync");
if (!str.empty()) {
logger_enable_fsync(hlog, hv_getboolean(str.c_str()));
}
hlogi("%s version: %s", g_main_ctx.program_name, hv_compile_version());
hlog_fsync();
// worker_processes
int worker_processes = 0;
#ifdef DEBUG
// Disable multi-processes mode for debugging
worker_processes = 0;
#else
str = ini.GetValue("worker_processes");
if (str.size() != 0) {
if (strcmp(str.c_str(), "auto") == 0) {
worker_processes = get_ncpu();
hlogd("worker_processes=ncpu=%d", worker_processes);
}
else {
worker_processes = atoi(str.c_str());
}
}
#endif
g_http_server.worker_processes = LIMIT(0, worker_processes, MAXNUM_WORKER_PROCESSES);
// worker_threads
int worker_threads = 0;
str = ini.GetValue("worker_threads");
if (str.size() != 0) {
if (strcmp(str.c_str(), "auto") == 0) {
worker_threads = get_ncpu();
hlogd("worker_threads=ncpu=%d", worker_threads);
}
else {
worker_threads = atoi(str.c_str());
}
}
g_http_server.worker_threads = LIMIT(0, worker_threads, 64);
// worker_connections
str = ini.GetValue("worker_connections");
if (str.size() != 0) {
g_http_server.worker_connections = atoi(str.c_str());
}
// http_port
int port = 0;
const char* szPort = get_arg("p");
if (szPort) {
port = atoi(szPort);
}
if (port == 0) {
port = ini.Get<int>("port");
}
if (port == 0) {
port = ini.Get<int>("http_port");
}
g_http_server.port = port;
// https_port
if (HV_WITH_SSL) {
g_http_server.https_port = ini.Get<int>("https_port");
}
if (g_http_server.port == 0 && g_http_server.https_port == 0) {
printf("Please config listen port!\n");
exit(-10);
}
// base_url
str = ini.GetValue("base_url");
if (str.size() != 0) {
g_http_service.base_url = str;
}
// document_root
str = ini.GetValue("document_root");
if (str.size() != 0) {
g_http_service.document_root = str;
}
// home_page
str = ini.GetValue("home_page");
if (str.size() != 0) {
g_http_service.home_page = str;
}
// error_page
str = ini.GetValue("error_page");
if (str.size() != 0) {
g_http_service.error_page = str;
}
// index_of
str = ini.GetValue("index_of");
if (str.size() != 0) {
g_http_service.index_of = str;
}
// keepalive_timeout
str = ini.GetValue("keepalive_timeout");
if (str.size() != 0) {
g_http_service.keepalive_timeout = atoi(str.c_str());
}
// limit_rate
str = ini.GetValue("limit_rate");
if (str.size() != 0) {
g_http_service.limit_rate = atoi(str.c_str());
}
// access_log
str = ini.GetValue("access_log");
if (str.size() != 0) {
g_http_service.enable_access_log = hv_getboolean(str.c_str());
}
// cors
if (ini.Get<bool>("cors")) {
g_http_service.AllowCORS();
}
// ssl
if (g_http_server.https_port > 0) {
std::string crt_file = ini.GetValue("ssl_certificate");
std::string key_file = ini.GetValue("ssl_privatekey");
std::string ca_file = ini.GetValue("ssl_ca_certificate");
hlogi("SSL backend is %s", hssl_backend());
hssl_ctx_opt_t param;
memset(&param, 0, sizeof(param));
param.crt_file = crt_file.c_str();
param.key_file = key_file.c_str();
param.ca_file = ca_file.c_str();
param.endpoint = HSSL_SERVER;
if (g_http_server.newSslCtx(&param) != 0) {
#ifdef OS_WIN
if (strcmp(hssl_backend(), "schannel") == 0) {
hlogw("schannel needs pkcs12 formatted certificate file.");
g_http_server.https_port = 0;
}
#else
hloge("SSL certificate verify failed!");
exit(0);
#endif
}
else {
hlogi("SSL certificate verify ok!");
}
}
// proxy
auto proxy_keys = ini.GetKeys("proxy");
for (const auto& proxy_key : proxy_keys) {
str = ini.GetValue(proxy_key, "proxy");
if (str.empty()) continue;
if (proxy_key[0] == '/') {
// reverse proxy
const std::string& path = proxy_key;
std::string proxy_url = hv::ltrim(str, "> ");
hlogi("reverse_proxy %s => %s", path.c_str(), proxy_url.c_str());
g_http_service.Proxy(path.c_str(), proxy_url.c_str());
}
else if (strcmp(proxy_key.c_str(), "proxy_connect_timeout") == 0) {
g_http_service.proxy_connect_timeout = atoi(str.c_str());
}
else if (strcmp(proxy_key.c_str(), "proxy_read_timeout") == 0) {
g_http_service.proxy_read_timeout = atoi(str.c_str());
}
else if (strcmp(proxy_key.c_str(), "proxy_write_timeout") == 0) {
g_http_service.proxy_write_timeout = atoi(str.c_str());
}
else if (strcmp(proxy_key.c_str(), "forward_proxy") == 0) {
hlogi("forward_proxy = %s", str.c_str());
if (hv_getboolean(str.c_str())) {
g_http_service.EnableForwardProxy();
}
}
else if (strcmp(proxy_key.c_str(), "trust_proxies") == 0) {
auto trust_proxies = hv::split(str, ';');
for (auto trust_proxy : trust_proxies) {
trust_proxy = hv::trim(trust_proxy);
if (trust_proxy.empty()) continue;
hlogi("trust_proxy %s", trust_proxy.c_str());
g_http_service.AddTrustProxy(trust_proxy.c_str());
}
}
else if (strcmp(proxy_key.c_str(), "no_proxies") == 0) {
auto no_proxies = hv::split(str, ';');
for (auto no_proxy : no_proxies) {
no_proxy = hv::trim(no_proxy);
if (no_proxy.empty()) continue;
hlogi("no_proxy %s", no_proxy.c_str());
g_http_service.AddNoProxy(no_proxy.c_str());
}
}
}
hlogi("parse_confile('%s') OK", confile);
return 0;
}
static void on_reload(void* userdata) {
hlogi("reload confile [%s]", g_main_ctx.confile);
parse_confile(g_main_ctx.confile);
}
int main(int argc, char** argv) {
// g_main_ctx
main_ctx_init(argc, argv);
int ret = parse_opt_long(argc, argv, long_options, ARRAY_SIZE(long_options));
if (ret != 0) {
print_help();
exit(ret);
}
// help
if (get_arg("h")) {
print_help();
exit(0);
}
// version
if (get_arg("v")) {
print_version();
exit(0);
}
// parse_confile
const char* confile = get_arg("c");
if (confile) {
strncpy(g_main_ctx.confile, confile, sizeof(g_main_ctx.confile));
}
parse_confile(g_main_ctx.confile);
// test
if (get_arg("t")) {
printf("Test confile [%s] OK!\n", g_main_ctx.confile);
exit(0);
}
// signal
signal_init(on_reload);
const char* signal = get_arg("s");
if (signal) {
signal_handle(signal);
}
#ifdef OS_UNIX
// daemon
if (get_arg("d")) {
// nochdir, noclose
int ret = daemon(1, 1);
if (ret != 0) {
printf("daemon error: %d\n", ret);
exit(-10);
}
}
#endif
// pidfile
create_pidfile();
// http_server
Router::Register(g_http_service);
g_http_server.registerHttpService(&g_http_service);
#if 0
std::atomic_flag init_flag = ATOMIC_FLAG_INIT;
g_http_server.onWorkerStart = [&init_flag](){
if (!init_flag.test_and_set()) {
hv::async::startup();
}
};
g_http_server.onWorkerStop = [&init_flag](){
if (init_flag.test_and_set()) {
hv::async::cleanup();
}
};
#endif
g_http_server.run();
return ret;
}

148
third_party/libhv/examples/httpd/router.cpp vendored Executable file
View File

@@ -0,0 +1,148 @@
#include "router.h"
#include "handler.h"
#include "hthread.h" // import hv_gettid
#include "hasync.h" // import hv::async
#include "requests.h" // import requests::async
void Router::Register(hv::HttpService& router) {
/* handler chain */
// preprocessor -> middleware -> processor -> postprocessor
// processor: pathHandlers -> staticHandler -> errorHandler
router.preprocessor = Handler::preprocessor;
router.postprocessor = Handler::postprocessor;
// router.errorHandler = Handler::errorHandler;
// router.largeFileHandler = Handler::sendLargeFile;
// middleware
// router.Use(Handler::Authorization);
// curl -v http://ip:port/ping
router.GET("/ping", [](HttpRequest* req, HttpResponse* resp) {
return resp->String("pong");
});
// curl -v http://ip:port/data
router.GET("/data", [](HttpRequest* req, HttpResponse* resp) {
static char data[] = "0123456789";
return resp->Data(data, 10 /*, false */);
});
// curl -v http://ip:port/html/index.html
router.GET("/html/index.html", [](HttpRequest* req, HttpResponse* resp) {
return resp->File("html/index.html");
});
// curl -v http://ip:port/paths
router.GET("/paths", [&router](HttpRequest* req, HttpResponse* resp) {
return resp->Json(router.Paths());
});
// curl -v http://ip:port/service
router.GET("/service", [](const HttpContextPtr& ctx) {
ctx->setContentType("application/json");
ctx->set("base_url", ctx->service->base_url);
ctx->set("document_root", ctx->service->document_root);
ctx->set("home_page", ctx->service->home_page);
ctx->set("error_page", ctx->service->error_page);
ctx->set("index_of", ctx->service->index_of);
return 200;
});
// curl -v http://ip:port/get?env=1
router.GET("/get", [](const HttpContextPtr& ctx) {
hv::Json resp;
resp["origin"] = ctx->ip();
resp["url"] = ctx->url();
resp["args"] = ctx->params();
resp["headers"] = ctx->headers();
return ctx->send(resp.dump(2));
});
// curl -v http://ip:port/echo -d "hello,world!"
router.POST("/echo", [](const HttpContextPtr& ctx) {
return ctx->send(ctx->body(), ctx->type());
});
// wildcard *
// curl -v http://ip:port/wildcard/any
router.GET("/wildcard*", [](HttpRequest* req, HttpResponse* resp) {
std::string str = req->path + " match /wildcard*";
return resp->String(str);
});
// curl -v http://ip:port/async
router.GET("/async", [](const HttpRequestPtr& req, const HttpResponseWriterPtr& writer) {
writer->Begin();
writer->WriteHeader("X-Response-tid", hv_gettid());
writer->WriteHeader("Content-Type", "text/plain");
writer->WriteBody("This is an async response.\n");
writer->End();
});
// curl -v http://ip:port/www.*
// curl -v http://ip:port/www.example.com
router.GET("/www.*", [](const HttpRequestPtr& req, const HttpResponseWriterPtr& writer) {
auto req2 = std::make_shared<HttpRequest>();
req2->url = req->path.substr(1);
requests::async(req2, [writer](const HttpResponsePtr& resp2){
writer->Begin();
if (resp2 == NULL) {
writer->WriteStatus(HTTP_STATUS_NOT_FOUND);
writer->WriteHeader("Content-Type", "text/html");
writer->WriteBody("<center><h1>404 Not Found</h1></center>");
} else {
writer->WriteResponse(resp2.get());
}
writer->End();
});
});
// curl -v http://ip:port/sleep?t=1000
router.GET("/sleep", Handler::sleep);
// curl -v http://ip:port/setTimeout?t=1000
router.GET("/setTimeout", Handler::setTimeout);
// curl -v http://ip:port/query?page_no=1\&page_size=10
router.GET("/query", Handler::query);
// Content-Type: application/x-www-form-urlencoded
// curl -v http://ip:port/kv -H "content-type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'
router.POST("/kv", Handler::kv);
// Content-Type: application/json
// curl -v http://ip:port/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'
router.POST("/json", Handler::json);
// Content-Type: multipart/form-data
// bin/curl -v http://ip:port/form -F 'user=admin' -F 'pswd=123456'
router.POST("/form", Handler::form);
// curl -v http://ip:port/test -H "Content-Type:application/x-www-form-urlencoded" -d 'bool=1&int=123&float=3.14&string=hello'
// curl -v http://ip:port/test -H "Content-Type:application/json" -d '{"bool":true,"int":123,"float":3.14,"string":"hello"}'
// bin/curl -v http://ip:port/test -F 'bool=1' -F 'int=123' -F 'float=3.14' -F 'string=hello'
router.POST("/test", Handler::test);
// Content-Type: application/grpc
// bin/curl -v --http2 http://ip:port/grpc -H "content-type:application/grpc" -d 'protobuf'
router.POST("/grpc", Handler::grpc);
// RESTful API: /group/:group_name/user/:user_id
// curl -v -X DELETE http://ip:port/group/test/user/123
router.Delete("/group/:group_name/user/:user_id", Handler::restful);
// router.Delete("/group/{group_name}/user/{user_id}", Handler::restful);
// curl -v http://ip:port/login -H "Content-Type:application/json" -d '{"username":"admin","password":"123456"}'
router.POST("/login", Handler::login);
// curl -v http://ip:port/upload?filename=LICENSE -d '@LICENSE'
// curl -v http://ip:port/upload -F 'file=@LICENSE'
router.POST("/upload", Handler::upload);
// curl -v http://ip:port/upload/README.md -d '@README.md'
router.POST("/upload/{filename}", Handler::recvLargeFile);
// SSE: Server Send Events
// @test html/EventSource.html EventSource.onmessage
router.GET("/sse", Handler::sse);
}

11
third_party/libhv/examples/httpd/router.h vendored Executable file
View File

@@ -0,0 +1,11 @@
#ifndef HV_HTTPD_ROUTER_H
#define HV_HTTPD_ROUTER_H
#include "HttpService.h"
class Router {
public:
static void Register(hv::HttpService& router);
};
#endif // HV_HTTPD_ROUTER_H