initial commit
This commit is contained in:
370
third_party/libhv/examples/httpd/handler.cpp
vendored
Executable file
370
third_party/libhv/examples/httpd/handler.cpp
vendored
Executable 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
56
third_party/libhv/examples/httpd/handler.h
vendored
Executable 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
340
third_party/libhv/examples/httpd/httpd.cpp
vendored
Executable 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(¶m, 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(¶m) != 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
148
third_party/libhv/examples/httpd/router.cpp
vendored
Executable 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
11
third_party/libhv/examples/httpd/router.h
vendored
Executable 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
|
||||
Reference in New Issue
Block a user