同步操作将从 libhv/libhv 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
#include "HttpHandler.h"#include "hversion.h"#include "herr.h"#include "hlog.h"#include "htime.h"#include "hurl.h"#include "hasync.h" // import hv::async for http_async_handler#include "httpdef.h"#include "http2def.h"#include "wsdef.h"#include "http_page.h"#include "EventLoop.h" // import hv::setIntervalusing namespace hv;#define MIN_HTTP_REQUEST "GET / HTTP/1.1\r\n\r\n"#define MIN_HTTP_REQUEST_LEN 14 // exclude CRLF#define HTTP_100_CONTINUE_RESPONSE "HTTP/1.1 100 Continue\r\n\r\n"#define HTTP_100_CONTINUE_RESPONSE_LEN 25#define HTTP_200_CONNECT_RESPONSE "HTTP/1.1 200 Connection established\r\n\r\n"#define HTTP_200_CONNECT_RESPONSE_LEN 39HttpHandler::HttpHandler(hio_t* io) :protocol(HttpHandler::UNKNOWN),state(WANT_RECV),error(0),// flagsssl(0),keepalive(1),upgrade(0),proxy(0),proxy_connected(0),forward_proxy(0),reverse_proxy(0),ip{'0円'},port(0),pid(0),tid(0),// for httpio(io),service(NULL),api_handler(NULL),// for websocketws_service(NULL),last_send_ping_time(0),last_recv_pong_time(0),// for sendfilefiles(NULL),file(NULL),// for proxyproxy_port(0){// Init();}HttpHandler::~HttpHandler() {Close();}bool HttpHandler::Init(int http_version) {parser.reset(HttpParser::New(HTTP_SERVER, (enum http_version)http_version));if (parser == NULL) {return false;}req = std::make_shared<HttpRequest>();resp = std::make_shared<HttpResponse>();if(http_version == 1) {protocol = HTTP_V1;} else if (http_version == 2) {protocol = HTTP_V2;resp->http_major = req->http_major = 2;resp->http_minor = req->http_minor = 0;}if (io) {hloop_t* loop = hevent_loop(io);pid = hloop_pid(loop);tid = hloop_tid(loop);writer = std::make_shared<HttpResponseWriter>(io, resp);writer->status = hv::SocketChannel::CONNECTED;} else {pid = hv_getpid();tid = hv_gettid();}parser->InitRequest(req.get());// NOTE: hook http_cbreq->http_cb = [this](HttpMessage* msg, http_parser_state state, const char* data, size_t size) {if (this->state == WANT_CLOSE) return;switch (state) {case HP_HEADERS_COMPLETE:if (this->error != 0) return;onHeadersComplete();break;case HP_BODY:if (this->error != 0) return;onBody(data, size);break;case HP_MESSAGE_COMPLETE:onMessageComplete();break;default:break;}};return true;}void HttpHandler::Reset() {state = WANT_RECV;error = 0;req->Reset();resp->Reset();ctx = NULL;api_handler = NULL;closeFile();if (writer) {writer->Begin();writer->onwrite = NULL;writer->onclose = NULL;}parser->InitRequest(req.get());}void HttpHandler::Close() {if (writer) {writer->status = hv::SocketChannel::DISCONNECTED;}if (api_handler && api_handler->state_handler) {if (parser && !parser->IsComplete()) {api_handler->state_handler(context(), HP_ERROR, NULL, 0);}return;}// close proxycloseProxy();// close filecloseFile();// oncloseif (protocol == HttpHandler::WEBSOCKET) {WebSocketOnClose();} else {if (writer && writer->onclose) {writer->onclose();}}}bool HttpHandler::SwitchHTTP2() {HttpParser* http2_parser = HttpParser::New(HTTP_SERVER, ::HTTP_V2);if (http2_parser == NULL) {return false;}parser.reset(http2_parser);protocol = HTTP_V2;resp->http_major = req->http_major = 2;resp->http_minor = req->http_minor = 0;parser->InitRequest(req.get());return true;}bool HttpHandler::SwitchWebSocket() {if(!io) return false;protocol = WEBSOCKET;ws_parser = std::make_shared<WebSocketParser>();ws_channel = std::make_shared<WebSocketChannel>(io, WS_SERVER);ws_parser->onMessage = [this](int opcode, const std::string& msg){ws_channel->opcode = (enum ws_opcode)opcode;switch(opcode) {case WS_OPCODE_CLOSE:ws_channel->send(msg, WS_OPCODE_CLOSE);ws_channel->close();break;case WS_OPCODE_PING:// printf("recv ping\n");// printf("send pong\n");ws_channel->send(msg, WS_OPCODE_PONG);break;case WS_OPCODE_PONG:// printf("recv pong\n");this->last_recv_pong_time = gethrtime_us();break;case WS_OPCODE_TEXT:case WS_OPCODE_BINARY:// onmessageif (ws_service && ws_service->onmessage) {ws_service->onmessage(ws_channel, msg);}break;default:break;}};// NOTE: cancel keepalive timer, judge alive by heartbeat.ws_channel->setKeepaliveTimeout(0);if (ws_service && ws_service->ping_interval > 0) {int ping_interval = MAX(ws_service->ping_interval, 1000);ws_channel->setHeartbeat(ping_interval, [this](){if (last_recv_pong_time < last_send_ping_time) {hlogw("[%s:%d] websocket no pong!", ip, port);ws_channel->close();} else {// printf("send ping\n");ws_channel->sendPing();last_send_ping_time = gethrtime_us();}});}return true;}const HttpContextPtr& HttpHandler::context() {if (!ctx) {ctx = std::make_shared<hv::HttpContext>();ctx->service = service;ctx->request = req;ctx->response = resp;ctx->writer = writer;}return ctx;}int HttpHandler::customHttpHandler(const http_handler& handler) {return invokeHttpHandler(&handler);}int HttpHandler::invokeHttpHandler(const http_handler* handler) {int status_code = HTTP_STATUS_NOT_IMPLEMENTED;if (handler->sync_handler) {// NOTE: sync_handler run on IO threadstatus_code = handler->sync_handler(req.get(), resp.get());} else if (handler->async_handler) {// NOTE: async_handler run on hv::async threadpoolhv::async(std::bind(handler->async_handler, req, writer));status_code = HTTP_STATUS_NEXT;} else if (handler->ctx_handler) {// NOTE: ctx_handler run on IO thread, you can easily post HttpContextPtr to your consumer thread for processing.status_code = handler->ctx_handler(context());} else if (handler->state_handler) {status_code = handler->state_handler(context(), HP_MESSAGE_COMPLETE, NULL, 0);}return status_code;}void HttpHandler::onHeadersComplete() {// printf("onHeadersComplete\n");int status_code = handleRequestHeaders();if (status_code != HTTP_STATUS_OK) {error = ERR_REQUEST;return;}HttpRequest* pReq = req.get();if (service && service->pathHandlers.size() != 0) {service->GetRoute(pReq, &api_handler);}if (api_handler && api_handler->state_handler) {api_handler->state_handler(context(), HP_HEADERS_COMPLETE, NULL, 0);return;}if (proxy) {handleProxy();return;}// Expect: 100-continuehandleExpect100();}void HttpHandler::onBody(const char* data, size_t size) {if (api_handler && api_handler->state_handler) {api_handler->state_handler(context(), HP_BODY, data, size);return;}if (proxy && proxy_connected) {if (io) hio_write_upstream(io, (void*)data, size);return;}req->body.append(data, size);return;}void HttpHandler::onMessageComplete() {// printf("onMessageComplete\n");int status_code = HTTP_STATUS_OK;if (error) {SendHttpStatusResponse(resp->status_code);return;}if (proxy) {if (proxy_connected) Reset();return;}addResponseHeaders();// upgrade ? handleUpgrade : HandleHttpRequestif (upgrade) {auto iter_upgrade = req->headers.find("upgrade");if (iter_upgrade != req->headers.end()) {handleUpgrade(iter_upgrade->second.c_str());status_code = resp->status_code;}} else {status_code = HandleHttpRequest();if (status_code != HTTP_STATUS_NEXT) {SendHttpResponse();}}// access logif (service && service->enable_access_log) {hlogi("[%ld-%ld][%s:%d][%s %s]=>[%d %s]",pid, tid, ip, port,http_method_str(req->method), req->path.c_str(),resp->status_code, resp->status_message());}if (status_code != HTTP_STATUS_NEXT) {// keepalive ? Reset : Closeif (keepalive) {Reset();} else {state = WANT_CLOSE;}}}int HttpHandler::handleRequestHeaders() {HttpRequest* pReq = req.get();pReq->scheme = ssl ? "https" : "http";pReq->client_addr.ip = ip;pReq->client_addr.port = port;// keepalivekeepalive = pReq->IsKeepAlive();// upgradeupgrade = pReq->IsUpgrade();// proxyproxy = forward_proxy = reverse_proxy = 0;if (hv::startswith(pReq->url, "http")) {// forward proxyproxy = forward_proxy = 1;}else if (pReq->method == HTTP_CONNECT) {// proxy tunnel// CONNECT ip:port HTTP/1.1\r\npReq->url = "https://" + pReq->url;proxy = forward_proxy = 1;keepalive = true;}// printf("url=%s\n", pReq->url.c_str());pReq->ParseUrl();// printf("path=%s\n", pReq->path.c_str());// fix CVE-2023-26147if (pReq->path.find("%") != std::string::npos) {std::string unescaped_path = HUrl::unescape(pReq->path);if (unescaped_path.find("\r\n") != std::string::npos) {hlogw("Illegal path: %s\n", unescaped_path.c_str());resp->status_code = HTTP_STATUS_BAD_REQUEST;return resp->status_code;}}if (proxy) {// Proxy-Connectionauto iter = pReq->headers.find("Proxy-Connection");if (iter != pReq->headers.end()) {const char* keepalive_value = iter->second.c_str();if (stricmp(keepalive_value, "keep-alive") == 0) {keepalive = true;}else if (stricmp(keepalive_value, "close") == 0) {keepalive = false;}else if (stricmp(keepalive_value, "upgrade") == 0) {keepalive = true;}}}else {// reverse proxystd::string proxy_url = service->GetProxyUrl(pReq->path.c_str());if (!proxy_url.empty()) {pReq->url = proxy_url;proxy = reverse_proxy = 1;}}// TODO: rewrite urlreturn HTTP_STATUS_OK;}void HttpHandler::handleExpect100() {// Expect: 100-continueauto iter = req->headers.find("Expect");if (iter != req->headers.end() &&stricmp(iter->second.c_str(), "100-continue") == 0) {if (io) hio_write(io, HTTP_100_CONTINUE_RESPONSE, HTTP_100_CONTINUE_RESPONSE_LEN);}}void HttpHandler::addResponseHeaders() {HttpResponse* pResp = resp.get();// Server:pResp->headers["Server"] = "libhv/" HV_VERSION_STRING;// Connection:pResp->headers["Connection"] = keepalive ? "keep-alive" : "close";}int HttpHandler::HandleHttpRequest() {// preprocessor -> middleware -> processor -> postprocessorHttpRequest* pReq = req.get();HttpResponse* pResp = resp.get();// NOTE: Not all users want to parse body, we comment it out.// pReq->ParseBody();int status_code = pResp->status_code;if (status_code != HTTP_STATUS_OK) {goto postprocessor;}preprocessor:state = HANDLE_BEGIN;if (service->preprocessor) {status_code = customHttpHandler(service->preprocessor);if (status_code != HTTP_STATUS_NEXT) {goto postprocessor;}}middleware:for (const auto& middleware : service->middleware) {status_code = customHttpHandler(middleware);if (status_code != HTTP_STATUS_NEXT) {goto postprocessor;}}processor:if (service->processor) {status_code = customHttpHandler(service->processor);} else {status_code = defaultRequestHandler();}postprocessor:if (status_code >= 100 && status_code < 600) {pResp->status_code = (http_status)status_code;if (pResp->status_code >= 400 && pResp->body.size() == 0 && pReq->method != HTTP_HEAD) {if (service->errorHandler) {customHttpHandler(service->errorHandler);} else {defaultErrorHandler();}}}if (fc) {pResp->content = fc->filebuf.base;pResp->content_length = fc->filebuf.len;pResp->headers["Content-Type"] = fc->content_type;pResp->headers["Last-Modified"] = fc->last_modified;pResp->headers["Etag"] = fc->etag;}if (service->postprocessor) {customHttpHandler(service->postprocessor);}if (writer && writer->state != hv::HttpResponseWriter::SEND_BEGIN) {status_code = HTTP_STATUS_NEXT;}if (status_code == HTTP_STATUS_NEXT) {state = HANDLE_CONTINUE;} else {state = HANDLE_END;}return status_code;}int HttpHandler::defaultRequestHandler() {int status_code = HTTP_STATUS_OK;if (api_handler) {status_code = invokeHttpHandler(api_handler);}else if (req->method == HTTP_GET || req->method == HTTP_HEAD) {// static handlerif (service->staticHandler) {status_code = customHttpHandler(service->staticHandler);}else if (service->staticDirs.size() > 0) {status_code = defaultStaticHandler();}else {status_code = HTTP_STATUS_NOT_FOUND;}}else {// Not Implementedstatus_code = HTTP_STATUS_NOT_IMPLEMENTED;}return status_code;}int HttpHandler::defaultStaticHandler() {// file servicestd::string path = req->Path();const char* req_path = path.c_str();// path safe checkif (req_path[0] != '/' || strstr(req_path, "/..") || strstr(req_path, "\\..")) {return HTTP_STATUS_BAD_REQUEST;}std::string filepath;bool is_dir = path.back() == '/' &&service->index_of.size() > 0 &&hv_strstartswith(req_path, service->index_of.c_str());if (is_dir) {filepath = service->document_root + path;} else {filepath = service->GetStaticFilepath(req_path);}if (filepath.empty()) {return HTTP_STATUS_NOT_FOUND;}int status_code = HTTP_STATUS_OK;// Range:bool has_range = false;long from, to = 0;if (req->GetRange(from, to)) {has_range = true;if (openFile(filepath.c_str()) != 0) {return HTTP_STATUS_NOT_FOUND;}long total = file->size();if (to == 0 || to >= total) to = total - 1;file->seek(from);status_code = HTTP_STATUS_PARTIAL_CONTENT;resp->status_code = HTTP_STATUS_PARTIAL_CONTENT;resp->content_length = to - from + 1;resp->SetContentTypeByFilename(filepath.c_str());resp->SetRange(from, to, total);if(resp->content_length < service->max_file_cache_size) {// read into body directlyint nread = file->readrange(resp->body, from, to);closeFile();if (nread != resp->content_length) {resp->content_length = 0;resp->body.clear();return HTTP_STATUS_INTERNAL_SERVER_ERROR;}}else {if (service->largeFileHandler) {status_code = customHttpHandler(service->largeFileHandler);} else {status_code = defaultLargeFileHandler();}}return status_code;}// FileCacheFileCache::OpenParam param;param.max_read = service->max_file_cache_size;param.need_read = !(req->method == HTTP_HEAD || has_range);param.path = req_path;if (files) {fc = files->Open(filepath.c_str(), ¶m);}if (fc == NULL) {if (param.error == ERR_OVER_LIMIT) {if (service->largeFileHandler) {status_code = customHttpHandler(service->largeFileHandler);} else {status_code = defaultLargeFileHandler();}} else {status_code = HTTP_STATUS_NOT_FOUND;}}else {// Not Modifiedauto iter = req->headers.find("if-none-match");if (iter != req->headers.end() &&strcmp(iter->second.c_str(), fc->etag) == 0) {fc = NULL;return HTTP_STATUS_NOT_MODIFIED;}iter = req->headers.find("if-modified-since");if (iter != req->headers.end() &&strcmp(iter->second.c_str(), fc->last_modified) == 0) {fc = NULL;return HTTP_STATUS_NOT_MODIFIED;}}return status_code;}int HttpHandler::defaultLargeFileHandler() {if (!writer) return HTTP_STATUS_NOT_IMPLEMENTED;if (!isFileOpened()) {std::string filepath = service->GetStaticFilepath(req->Path().c_str());if (filepath.empty() || openFile(filepath.c_str()) != 0) {return HTTP_STATUS_NOT_FOUND;}resp->content_length = file->size();resp->SetContentTypeByFilename(filepath.c_str());}if (service->limit_rate == 0) {// forbidden to send large fileresp->content_length = 0;resp->status_code = HTTP_STATUS_FORBIDDEN;} else {size_t bufsize = 40960; // 40Kfile->buf.resize(bufsize);if (service->limit_rate < 0) {// unlimited: sendFile when writablewriter->onwrite = [this](HBuf* buf) {if (writer->isWriteComplete()) {sendFile();}};} else {// limit_rate=40KB/s interval_ms=1000// limit_rate=500KB/s interval_ms=80int interval_ms = file->buf.len * 1000 / 1024 / service->limit_rate;// limit_rate=40MB/s interval_m=1: 40KB/ms = 40MB/s = 320Mbpsif (interval_ms == 0) interval_ms = 1;// printf("limit_rate=%dKB/s interval_ms=%d\n", service->limit_rate, interval_ms);file->timer = setInterval(interval_ms, std::bind(&HttpHandler::sendFile, this));}}writer->EndHeaders();return HTTP_STATUS_UNFINISHED;}int HttpHandler::defaultErrorHandler() {// error pageif (service->error_page.size() != 0) {std::string filepath = service->document_root + '/' + service->error_page;if (files) {// cache and load error pageFileCache::OpenParam param;fc = files->Open(filepath.c_str(), ¶m);}}// status pageif (fc == NULL && resp->body.size() == 0) {resp->content_type = TEXT_HTML;make_http_status_page(resp->status_code, resp->body);}return 0;}int HttpHandler::FeedRecvData(const char* data, size_t len) {if (protocol == HttpHandler::UNKNOWN) {int http_version = 1;#if WITH_NGHTTP2if (strncmp(data, HTTP2_MAGIC, MIN(len, HTTP2_MAGIC_LEN)) == 0) {http_version = 2;}#else// check request-lineif (len < MIN_HTTP_REQUEST_LEN) {hloge("[%s:%d] http request-line too small", ip, port);error = ERR_REQUEST;return -1;}for (int i = 0; i < MIN_HTTP_REQUEST_LEN; ++i) {if (!IS_GRAPH(data[i])) {hloge("[%s:%d] http request-line not plain", ip, port);error = ERR_REQUEST;return -1;}}#endifif (!Init(http_version)) {hloge("[%s:%d] unsupported HTTP%d", ip, port, http_version);error = ERR_INVALID_PROTOCOL;return -1;}}int nfeed = 0;switch (protocol) {case HttpHandler::HTTP_V1:case HttpHandler::HTTP_V2:if (state != WANT_RECV) {Reset();}nfeed = parser->FeedRecvData(data, len);// printf("FeedRecvData %d=>%d\n", (int)len, nfeed);if (nfeed != len) {hloge("[%s:%d] http parse error: %s", ip, port, parser->StrError(parser->GetError()));error = ERR_PARSE;return -1;}break;case HttpHandler::WEBSOCKET:nfeed = ws_parser->FeedRecvData(data, len);if (nfeed != len) {hloge("[%s:%d] websocket parse error!", ip, port);error = ERR_PARSE;return -1;}break;default:hloge("[%s:%d] unknown protocol", ip, port);error = ERR_INVALID_PROTOCOL;return -1;}if (state == WANT_CLOSE) return 0;return error ? -1 : nfeed;}int HttpHandler::GetSendData(char** data, size_t* len) {if (state == HANDLE_CONTINUE) {return 0;}HttpRequest* pReq = req.get();HttpResponse* pResp = resp.get();if (protocol == HTTP_V1) {switch(state) {case WANT_RECV:if (parser->IsComplete()) state = WANT_SEND;else return 0;case HANDLE_END:state = WANT_SEND;case WANT_SEND:state = SEND_HEADER;case SEND_HEADER:{size_t content_length = 0;const char* content = NULL;// HEADif (pReq->method == HTTP_HEAD) {if (fc) {pResp->headers["Accept-Ranges"] = "bytes";pResp->headers["Content-Length"] = hv::to_string(fc->st.st_size);} else {pResp->headers["Content-Type"] = "text/html";pResp->headers["Content-Length"] = "0";}state = SEND_DONE;goto return_nobody;}// File serviceif (fc) {// FileCache// NOTE: no copy filebuf, more efficientheader = pResp->Dump(true, false);fc->prepend_header(header.c_str(), header.size());*data = fc->httpbuf.base;*len = fc->httpbuf.len;state = SEND_DONE;return *len;}// API servicecontent_length = pResp->ContentLength();content = (const char*)pResp->Content();if (content) {if (content_length > (1 << 20)) {state = SEND_BODY;goto return_header;} else {// NOTE: header+body in one package if <= 1Mheader = pResp->Dump(true, false);header.append(content, content_length);state = SEND_DONE;goto return_header;}} else {state = SEND_DONE;goto return_header;}return_nobody:pResp->content_length = 0;return_header:if (header.empty()) header = pResp->Dump(true, false);*data = (char*)header.c_str();*len = header.size();return *len;}case SEND_BODY:{*data = (char*)pResp->Content();*len = pResp->ContentLength();state = SEND_DONE;return *len;}case SEND_DONE:{// NOTE: remove file cache if > FILE_CACHE_MAX_SIZEif (fc && fc->filebuf.len > FILE_CACHE_MAX_SIZE) {files->Close(fc);}fc = NULL;header.clear();return 0;}default:return 0;}} else if (protocol == HTTP_V2) {int ret = parser->GetSendData(data, len);if (ret == 0) state = SEND_DONE;return ret;}return 0;}int HttpHandler::SendHttpResponse(bool submit) {if (!io || !parser) return -1;char* data = NULL;size_t len = 0, total_len = 0;if (submit) parser->SubmitResponse(resp.get());while (GetSendData(&data, &len)) {// printf("GetSendData %d\n", (int)len);if (data && len) {hio_write(io, data, len);total_len += len;}}return total_len;}int HttpHandler::SendHttpStatusResponse(http_status status_code) {if (state > WANT_SEND) return 0;resp->status_code = status_code;addResponseHeaders();HandleHttpRequest();state = WANT_SEND;return SendHttpResponse();}//------------------sendfile--------------------------------------int HttpHandler::openFile(const char* filepath) {closeFile();file = new LargeFile;file->timer = INVALID_TIMER_ID;return file->open(filepath, "rb");}bool HttpHandler::isFileOpened() {return file && file->isopen();}int HttpHandler::sendFile() {if (!writer || !writer->isWriteComplete() ||!isFileOpened() ||file->buf.len == 0 ||resp->content_length == 0) {return -1;}int readbytes = MIN(file->buf.len, resp->content_length);size_t nread = file->read(file->buf.base, readbytes);if (nread <= 0) {hloge("read file error!");error = ERR_READ_FILE;writer->close(true);return nread;}int nwrite = writer->WriteBody(file->buf.base, nread);if (nwrite < 0) {// disconnectdwriter->close(true);return nwrite;}resp->content_length -= nread;if (resp->content_length == 0) {writer->End();closeFile();}return nread;}void HttpHandler::closeFile() {if (file) {if (file->timer != INVALID_TIMER_ID) {killTimer(file->timer);file->timer = INVALID_TIMER_ID;}delete file;file = NULL;}}//------------------upgrade--------------------------------------int HttpHandler::handleUpgrade(const char* upgrade_protocol) {hlogi("[%s:%d] Upgrade: %s", ip, port, upgrade_protocol);// websocketif (stricmp(upgrade_protocol, "websocket") == 0) {return upgradeWebSocket();}// h2/h2cif (strnicmp(upgrade_protocol, "h2", 2) == 0) {return upgradeHTTP2();}hloge("[%s:%d] unsupported Upgrade: %s", ip, port, upgrade_protocol);return SetError(ERR_INVALID_PROTOCOL);}int HttpHandler::upgradeWebSocket() {/*HTTP/1.1 101 Switching ProtocolsConnection: UpgradeUpgrade: websocketSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=*/resp->status_code = HTTP_STATUS_SWITCHING_PROTOCOLS;resp->headers["Connection"] = "Upgrade";resp->headers["Upgrade"] = "websocket";// Sec-WebSocket-Accept:auto iter_key = req->headers.find(SEC_WEBSOCKET_KEY);if (iter_key != req->headers.end()) {char ws_accept[32] = {0};ws_encode_key(iter_key->second.c_str(), ws_accept);resp->headers[SEC_WEBSOCKET_ACCEPT] = ws_accept;}// Sec-WebSocket-Protocol:auto iter_protocol = req->headers.find(SEC_WEBSOCKET_PROTOCOL);if (iter_protocol != req->headers.end()) {hv::StringList subprotocols = hv::split(iter_protocol->second, ',');if (subprotocols.size() > 0) {hlogw("%s: %s => just select first protocol %s", SEC_WEBSOCKET_PROTOCOL, iter_protocol->second.c_str(), subprotocols[0].c_str());resp->headers[SEC_WEBSOCKET_PROTOCOL] = subprotocols[0];}}SendHttpResponse();if (!SwitchWebSocket()) {hloge("[%s:%d] unsupported websocket", ip, port);return SetError(ERR_INVALID_PROTOCOL);}// onopenWebSocketOnOpen();return 0;}int HttpHandler::upgradeHTTP2() {/*HTTP/1.1 101 Switching ProtocolsConnection: UpgradeUpgrade: h2c*/resp->status_code = HTTP_STATUS_SWITCHING_PROTOCOLS;resp->headers["Connection"] = "Upgrade";resp->headers["Upgrade"] = "h2c";SendHttpResponse();if (!SwitchHTTP2()) {hloge("[%s:%d] unsupported HTTP2", ip, port);return SetError(ERR_INVALID_PROTOCOL);}// NOTE: send HTTP2_SETTINGS frameSendHttpResponse(false);return 0;}//------------------proxy--------------------------------------int HttpHandler::handleProxy() {if (forward_proxy) {return handleForwardProxy();}if (reverse_proxy) {return handleReverseProxy();}return 0;}int HttpHandler::handleForwardProxy() {if (service && service->enable_forward_proxy) {return connectProxy(req->url);} else {hlogw("Forbidden to forward proxy %s", req->url.c_str());SetError(HTTP_STATUS_FORBIDDEN, HTTP_STATUS_FORBIDDEN);}return 0;}int HttpHandler::handleReverseProxy() {return connectProxy(req->url);}int HttpHandler::connectProxy(const std::string& strUrl) {if (!io) return ERR_NULL_POINTER;HUrl url;url.parse(strUrl);hlogi("[%s:%d] proxy_pass %s", ip, port, strUrl.c_str());if (proxy_connected) {if (url.host == proxy_host && url.port == proxy_port) {// reuse keepalive connectionsendProxyRequest();return 0;} else {// detach and close previous connectionhio_t* upstream_io = hio_get_upstream(io);if (upstream_io) {hio_setcb_close(upstream_io, NULL);closeProxy();}}}if (forward_proxy && !service->IsTrustProxy(url.host.c_str())) {hlogw("Forbidden to proxy %s", url.host.c_str());SetError(HTTP_STATUS_FORBIDDEN, HTTP_STATUS_FORBIDDEN);return 0;}hloop_t* loop = hevent_loop(io);proxy = 1;proxy_host = url.host;proxy_port = url.port;hio_t* upstream_io = hio_create_socket(loop, proxy_host.c_str(), proxy_port, HIO_TYPE_TCP, HIO_CLIENT_SIDE);if (upstream_io == NULL) {return SetError(ERR_SOCKET, HTTP_STATUS_BAD_GATEWAY);}if (url.scheme == "https") {hio_enable_ssl(upstream_io);}hevent_set_userdata(upstream_io, this);hio_setup_upstream(io, upstream_io);hio_setcb_connect(upstream_io, HttpHandler::onProxyConnect);hio_setcb_close(upstream_io, HttpHandler::onProxyClose);if (service->proxy_connect_timeout > 0) {hio_set_connect_timeout(upstream_io, service->proxy_connect_timeout);}if (service->proxy_read_timeout > 0) {hio_set_read_timeout(io, service->proxy_read_timeout);}if (service->proxy_write_timeout > 0) {hio_set_write_timeout(io, service->proxy_write_timeout);}hio_connect(upstream_io);// NOTE: wait upstream_io connected then start readhio_read_stop(io);return 0;}int HttpHandler::closeProxy() {if (proxy && proxy_connected) {proxy_connected = 0;if (io) hio_close_upstream(io);}return 0;}int HttpHandler::sendProxyRequest() {if (!io || !proxy_connected) return -1;req->headers.erase("Host");req->FillHost(proxy_host.c_str(), proxy_port);req->headers.erase("Proxy-Connection");req->headers["Connection"] = keepalive ? "keep-alive" : "close";req->headers["X-Real-IP"] = ip;// NOTE: send head + received bodystd::string msg = req->Dump(true, false) + req->body;// printf("%s\n", msg.c_str());req->Reset();hio_write_upstream(io, (void*)msg.c_str(), msg.size());if (parser->IsComplete()) state = WANT_SEND;return msg.size();}void HttpHandler::onProxyConnect(hio_t* upstream_io) {// printf("onProxyConnect\n");HttpHandler* handler = (HttpHandler*)hevent_userdata(upstream_io);hio_t* io = hio_get_upstream(upstream_io);assert(handler != NULL && io != NULL);handler->proxy_connected = 1;if (handler->req->method == HTTP_CONNECT) {// handler->resp->status_code = HTTP_STATUS_OK;// handler->SendHttpResponse();hio_write(io, HTTP_200_CONNECT_RESPONSE, HTTP_200_CONNECT_RESPONSE_LEN);handler->state = SEND_DONE;// NOTE: recv request then upstreamhio_setcb_read(io, hio_write_upstream);} else {handler->sendProxyRequest();}// NOTE: start recv request continue then upstreamif (handler->upgrade) hio_setcb_read(io, hio_write_upstream);hio_read_start(io);// NOTE: start recv response then upstreamhio_setcb_read(upstream_io, hio_write_upstream);hio_read_start(upstream_io);}void HttpHandler::onProxyClose(hio_t* upstream_io) {// printf("onProxyClose\n");HttpHandler* handler = (HttpHandler*)hevent_userdata(upstream_io);if (handler == NULL) return;handler->proxy_connected = 0;hevent_set_userdata(upstream_io, NULL);int error = hio_error(upstream_io);if (error == ETIMEDOUT) {handler->SendHttpStatusResponse(HTTP_STATUS_GATEWAY_TIMEOUT);}handler->error = error;hio_close_upstream(upstream_io);}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。