|
|
@@ -6,43 +6,54 @@
|
|
|
int HttpHandler::HandleHttpRequest() {
|
|
|
// preprocessor -> api -> web -> postprocessor
|
|
|
|
|
|
- int ret = 0;
|
|
|
- http_api_handler api = NULL;
|
|
|
+ int status_code = 200;
|
|
|
+ http_sync_handler sync_handler = NULL;
|
|
|
+ http_async_handler async_handler = NULL;
|
|
|
|
|
|
- req.ParseUrl();
|
|
|
- req.client_addr.ip = ip;
|
|
|
- req.client_addr.port = port;
|
|
|
+ HttpRequest* pReq = req.get();
|
|
|
+ HttpResponse* pResp = resp.get();
|
|
|
+
|
|
|
+ pReq->ParseUrl();
|
|
|
+ pReq->client_addr.ip = ip;
|
|
|
+ pReq->client_addr.port = port;
|
|
|
|
|
|
preprocessor:
|
|
|
+ state = HANDLE_BEGIN;
|
|
|
if (service->preprocessor) {
|
|
|
- ret = service->preprocessor(&req, &res);
|
|
|
- if (ret != 0) {
|
|
|
+ status_code = service->preprocessor(pReq, pResp);
|
|
|
+ if (status_code != 0) {
|
|
|
goto make_http_status_page;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (service->api_handlers.size() != 0) {
|
|
|
- service->GetApi(&req, &api);
|
|
|
+ service->GetApi(pReq, &sync_handler, &async_handler);
|
|
|
}
|
|
|
|
|
|
- if (api) {
|
|
|
- // api service
|
|
|
- ret = api(&req, &res);
|
|
|
- if (ret != 0) {
|
|
|
+ if (sync_handler) {
|
|
|
+ // sync api service
|
|
|
+ status_code = sync_handler(pReq, pResp);
|
|
|
+ if (status_code != 0) {
|
|
|
goto make_http_status_page;
|
|
|
}
|
|
|
}
|
|
|
+ else if (async_handler) {
|
|
|
+ // async api service
|
|
|
+ async_handler(req, writer);
|
|
|
+ status_code = 0;
|
|
|
+ }
|
|
|
else if (service->document_root.size() != 0 &&
|
|
|
- (req.method == HTTP_GET || req.method == HTTP_HEAD)) {
|
|
|
- // web service
|
|
|
- // path safe check
|
|
|
- const char* s = req.path.c_str();
|
|
|
+ (pReq->method == HTTP_GET || pReq->method == HTTP_HEAD)) {
|
|
|
+ // file service
|
|
|
+ status_code = 200;
|
|
|
+ const char* s = pReq->path.c_str();
|
|
|
const char* e = s;
|
|
|
while (*e && *e != '?' && *e != '#') ++e;
|
|
|
std::string path = std::string(s, e);
|
|
|
const char* req_path = path.c_str();
|
|
|
+ // path safe check
|
|
|
if (*req_path != '/' || strstr(req_path, "/../")) {
|
|
|
- res.status_code = HTTP_STATUS_BAD_REQUEST;
|
|
|
+ pResp->status_code = HTTP_STATUS_BAD_REQUEST;
|
|
|
goto make_http_status_page;
|
|
|
}
|
|
|
std::string filepath = service->document_root;
|
|
|
@@ -56,26 +67,26 @@ preprocessor:
|
|
|
is_index_of = true;
|
|
|
}
|
|
|
if (!is_dir || is_index_of) {
|
|
|
- bool need_read = req.method == HTTP_HEAD ? false : true;
|
|
|
+ bool need_read = pReq->method == HTTP_HEAD ? false : true;
|
|
|
fc = files->Open(filepath.c_str(), need_read, (void*)req_path);
|
|
|
}
|
|
|
if (fc == NULL) {
|
|
|
// Not Found
|
|
|
- ret = HTTP_STATUS_NOT_FOUND;
|
|
|
+ status_code = HTTP_STATUS_NOT_FOUND;
|
|
|
}
|
|
|
else {
|
|
|
// Not Modified
|
|
|
- auto iter = req.headers.find("if-not-match");
|
|
|
- if (iter != req.headers.end() &&
|
|
|
+ auto iter = pReq->headers.find("if-not-match");
|
|
|
+ if (iter != pReq->headers.end() &&
|
|
|
strcmp(iter->second.c_str(), fc->etag) == 0) {
|
|
|
- ret = HTTP_STATUS_NOT_MODIFIED;
|
|
|
+ status_code = HTTP_STATUS_NOT_MODIFIED;
|
|
|
fc = NULL;
|
|
|
}
|
|
|
else {
|
|
|
- iter = req.headers.find("if-modified-since");
|
|
|
- if (iter != req.headers.end() &&
|
|
|
+ iter = pReq->headers.find("if-modified-since");
|
|
|
+ if (iter != pReq->headers.end() &&
|
|
|
strcmp(iter->second.c_str(), fc->last_modified) == 0) {
|
|
|
- ret = HTTP_STATUS_NOT_MODIFIED;
|
|
|
+ status_code = HTTP_STATUS_NOT_MODIFIED;
|
|
|
fc = NULL;
|
|
|
}
|
|
|
}
|
|
|
@@ -83,14 +94,14 @@ preprocessor:
|
|
|
}
|
|
|
else {
|
|
|
// Not Implemented
|
|
|
- ret = HTTP_STATUS_NOT_IMPLEMENTED;
|
|
|
+ status_code = HTTP_STATUS_NOT_IMPLEMENTED;
|
|
|
}
|
|
|
|
|
|
make_http_status_page:
|
|
|
- if (ret >= 100 && ret < 600) {
|
|
|
- res.status_code = (http_status)ret;
|
|
|
+ if (status_code >= 100 && status_code < 600) {
|
|
|
+ pResp->status_code = (http_status)status_code;
|
|
|
}
|
|
|
- if (res.status_code >= 400 && res.body.size() == 0 && req.method != HTTP_HEAD) {
|
|
|
+ if (pResp->status_code >= 400 && pResp->body.size() == 0 && pReq->method != HTTP_HEAD) {
|
|
|
// error page
|
|
|
if (service->error_page.size() != 0) {
|
|
|
std::string filepath = service->document_root;
|
|
|
@@ -99,37 +110,70 @@ make_http_status_page:
|
|
|
fc = files->Open(filepath.c_str(), true, NULL);
|
|
|
}
|
|
|
// status page
|
|
|
- if (fc == NULL && res.body.size() == 0) {
|
|
|
- res.content_type = TEXT_HTML;
|
|
|
- make_http_status_page(res.status_code, res.body);
|
|
|
+ if (fc == NULL && pResp->body.size() == 0) {
|
|
|
+ pResp->content_type = TEXT_HTML;
|
|
|
+ make_http_status_page(pResp->status_code, pResp->body);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (fc) {
|
|
|
- res.content = fc->filebuf.base;
|
|
|
- res.content_length = fc->filebuf.len;
|
|
|
+ pResp->content = fc->filebuf.base;
|
|
|
+ pResp->content_length = fc->filebuf.len;
|
|
|
if (fc->content_type && *fc->content_type != '\0') {
|
|
|
- res.headers["Content-Type"] = fc->content_type;
|
|
|
+ pResp->headers["Content-Type"] = fc->content_type;
|
|
|
}
|
|
|
- res.headers["Last-Modified"] = fc->last_modified;
|
|
|
- res.headers["Etag"] = fc->etag;
|
|
|
+ pResp->headers["Last-Modified"] = fc->last_modified;
|
|
|
+ pResp->headers["Etag"] = fc->etag;
|
|
|
}
|
|
|
|
|
|
postprocessor:
|
|
|
if (service->postprocessor) {
|
|
|
- ret = service->postprocessor(&req, &res);
|
|
|
+ service->postprocessor(pReq, pResp);
|
|
|
}
|
|
|
|
|
|
- state = WANT_SEND;
|
|
|
- return ret;
|
|
|
+ if (status_code == 0) {
|
|
|
+ state = HANDLE_CONTINUE;
|
|
|
+ } else {
|
|
|
+ state = HANDLE_END;
|
|
|
+ parser->SubmitResponse(pResp);
|
|
|
+ }
|
|
|
+ return status_code;
|
|
|
+}
|
|
|
+
|
|
|
+int HttpHandler::FeedRecvData(const char* data, size_t len) {
|
|
|
+ int nfeed = 0;
|
|
|
+ if (protocol == HttpHandler::WEBSOCKET) {
|
|
|
+ nfeed = ws->parser->FeedRecvData(data, len);
|
|
|
+ if (nfeed != len) {
|
|
|
+ hloge("[%s:%d] websocket parse error!", ip, port);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (state != WANT_RECV) {
|
|
|
+ Reset();
|
|
|
+ }
|
|
|
+ nfeed = parser->FeedRecvData(data, len);
|
|
|
+ if (nfeed != len) {
|
|
|
+ hloge("[%s:%d] http parse error: %s", ip, port, parser->StrError(parser->GetError()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 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:
|
|
|
@@ -137,13 +181,13 @@ int HttpHandler::GetSendData(char** data, size_t* len) {
|
|
|
int content_length = 0;
|
|
|
const char* content = NULL;
|
|
|
// HEAD
|
|
|
- if (req.method == HTTP_HEAD) {
|
|
|
+ if (pReq->method == HTTP_HEAD) {
|
|
|
if (fc) {
|
|
|
- res.headers["Accept-Ranges"] = "bytes";
|
|
|
- res.headers["Content-Length"] = hv::to_string(fc->st.st_size);
|
|
|
+ pResp->headers["Accept-Ranges"] = "bytes";
|
|
|
+ pResp->headers["Content-Length"] = hv::to_string(fc->st.st_size);
|
|
|
} else {
|
|
|
- res.headers["Content-Type"] = "text/html";
|
|
|
- res.headers["Content-Length"] = "0";
|
|
|
+ pResp->headers["Content-Type"] = "text/html";
|
|
|
+ pResp->headers["Content-Length"] = "0";
|
|
|
}
|
|
|
state = SEND_DONE;
|
|
|
goto return_nobody;
|
|
|
@@ -153,29 +197,29 @@ int HttpHandler::GetSendData(char** data, size_t* len) {
|
|
|
long from, to, total;
|
|
|
int nread;
|
|
|
// Range:
|
|
|
- if (req.GetRange(from, to)) {
|
|
|
+ if (pReq->GetRange(from, to)) {
|
|
|
HFile file;
|
|
|
if (file.open(fc->filepath.c_str(), "rb") != 0) {
|
|
|
- res.status_code = HTTP_STATUS_NOT_FOUND;
|
|
|
+ pResp->status_code = HTTP_STATUS_NOT_FOUND;
|
|
|
state = SEND_DONE;
|
|
|
goto return_nobody;
|
|
|
}
|
|
|
total = file.size();
|
|
|
if (to == 0 || to >= total) to = total - 1;
|
|
|
- res.content_length = to - from + 1;
|
|
|
+ pResp->content_length = to - from + 1;
|
|
|
nread = file.readrange(body, from, to);
|
|
|
- if (nread != res.content_length) {
|
|
|
- res.status_code = HTTP_STATUS_INTERNAL_SERVER_ERROR;
|
|
|
+ if (nread != pResp->content_length) {
|
|
|
+ pResp->status_code = HTTP_STATUS_INTERNAL_SERVER_ERROR;
|
|
|
state = SEND_DONE;
|
|
|
goto return_nobody;
|
|
|
}
|
|
|
- res.SetRange(from, to, total);
|
|
|
+ pResp->SetRange(from, to, total);
|
|
|
state = SEND_BODY;
|
|
|
goto return_header;
|
|
|
}
|
|
|
// FileCache
|
|
|
// NOTE: no copy filebuf, more efficient
|
|
|
- header = res.Dump(true, false);
|
|
|
+ header = pResp->Dump(true, false);
|
|
|
fc->prepend_header(header.c_str(), header.size());
|
|
|
*data = fc->httpbuf.base;
|
|
|
*len = fc->httpbuf.len;
|
|
|
@@ -183,15 +227,15 @@ int HttpHandler::GetSendData(char** data, size_t* len) {
|
|
|
return *len;
|
|
|
}
|
|
|
// API service
|
|
|
- content_length = res.ContentLength();
|
|
|
- content = (const char*)res.Content();
|
|
|
+ content_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 <= 1M
|
|
|
- header = res.Dump(true, false);
|
|
|
+ header = pResp->Dump(true, false);
|
|
|
header.append(content, content_length);
|
|
|
state = SEND_DONE;
|
|
|
goto return_header;
|
|
|
@@ -201,9 +245,9 @@ int HttpHandler::GetSendData(char** data, size_t* len) {
|
|
|
goto return_header;
|
|
|
}
|
|
|
return_nobody:
|
|
|
- res.content_length = 0;
|
|
|
+ pResp->content_length = 0;
|
|
|
return_header:
|
|
|
- if (header.empty()) header = res.Dump(true, false);
|
|
|
+ if (header.empty()) header = pResp->Dump(true, false);
|
|
|
*data = (char*)header.c_str();
|
|
|
*len = header.size();
|
|
|
return *len;
|
|
|
@@ -211,8 +255,8 @@ return_header:
|
|
|
case SEND_BODY:
|
|
|
{
|
|
|
if (body.empty()) {
|
|
|
- *data = (char*)res.Content();
|
|
|
- *len = res.ContentLength();
|
|
|
+ *data = (char*)pResp->Content();
|
|
|
+ *len = pResp->ContentLength();
|
|
|
} else {
|
|
|
*data = (char*)body.c_str();
|
|
|
*len = body.size();
|