| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- #include "HttpHandler.h"
- #include "hbase.h"
- #include "http_page.h"
- int HttpHandler::HandleHttpRequest() {
- // preprocessor -> api -> web -> postprocessor
- int status_code = 200;
- http_sync_handler sync_handler = NULL;
- http_async_handler async_handler = NULL;
- 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) {
- status_code = service->preprocessor(pReq, pResp);
- if (status_code != 0) {
- goto make_http_status_page;
- }
- }
- if (service->api_handlers.size() != 0) {
- service->GetApi(pReq, &sync_handler, &async_handler);
- }
- 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 &&
- (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, "/../")) {
- pResp->status_code = HTTP_STATUS_BAD_REQUEST;
- goto make_http_status_page;
- }
- std::string filepath = service->document_root;
- filepath += req_path;
- if (req_path[1] == '\0') {
- filepath += service->home_page;
- }
- bool is_dir = filepath.c_str()[filepath.size()-1] == '/';
- bool is_index_of = false;
- if (service->index_of.size() != 0 && strstartswith(req_path, service->index_of.c_str())) {
- is_index_of = true;
- }
- if (!is_dir || is_index_of) {
- 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
- status_code = HTTP_STATUS_NOT_FOUND;
- }
- else {
- // Not Modified
- auto iter = pReq->headers.find("if-not-match");
- if (iter != pReq->headers.end() &&
- strcmp(iter->second.c_str(), fc->etag) == 0) {
- status_code = HTTP_STATUS_NOT_MODIFIED;
- fc = NULL;
- }
- else {
- iter = pReq->headers.find("if-modified-since");
- if (iter != pReq->headers.end() &&
- strcmp(iter->second.c_str(), fc->last_modified) == 0) {
- status_code = HTTP_STATUS_NOT_MODIFIED;
- fc = NULL;
- }
- }
- }
- }
- else {
- // Not Implemented
- status_code = HTTP_STATUS_NOT_IMPLEMENTED;
- }
- make_http_status_page:
- 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) {
- // error page
- if (service->error_page.size() != 0) {
- std::string filepath = service->document_root;
- filepath += '/';
- filepath += service->error_page;
- fc = files->Open(filepath.c_str(), true, NULL);
- }
- // status page
- if (fc == NULL && pResp->body.size() == 0) {
- pResp->content_type = TEXT_HTML;
- make_http_status_page(pResp->status_code, pResp->body);
- }
- }
- if (fc) {
- pResp->content = fc->filebuf.base;
- pResp->content_length = fc->filebuf.len;
- if (fc->content_type && *fc->content_type != '\0') {
- pResp->headers["Content-Type"] = fc->content_type;
- }
- pResp->headers["Last-Modified"] = fc->last_modified;
- pResp->headers["Etag"] = fc->etag;
- }
- postprocessor:
- if (service->postprocessor) {
- service->postprocessor(pReq, pResp);
- }
- 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:
- {
- int content_length = 0;
- const char* content = NULL;
- // HEAD
- if (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 service
- if (fc) {
- long from, to, total;
- int nread;
- // Range:
- if (pReq->GetRange(from, to)) {
- HFile file;
- if (file.open(fc->filepath.c_str(), "rb") != 0) {
- pResp->status_code = HTTP_STATUS_NOT_FOUND;
- state = SEND_DONE;
- goto return_nobody;
- }
- total = file.size();
- if (to == 0 || to >= total) to = total - 1;
- pResp->content_length = to - from + 1;
- nread = file.readrange(body, from, to);
- if (nread != pResp->content_length) {
- pResp->status_code = HTTP_STATUS_INTERNAL_SERVER_ERROR;
- state = SEND_DONE;
- goto return_nobody;
- }
- pResp->SetRange(from, to, total);
- state = SEND_BODY;
- goto return_header;
- }
- // FileCache
- // NOTE: no copy filebuf, more efficient
- header = 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 service
- 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 = 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:
- {
- if (body.empty()) {
- *data = (char*)pResp->Content();
- *len = pResp->ContentLength();
- } else {
- *data = (char*)body.c_str();
- *len = body.size();
- }
- state = SEND_DONE;
- return *len;
- }
- case SEND_DONE:
- {
- // NOTE: remove file cache if > 16M
- if (fc && fc->filebuf.len > (1 << 24)) {
- files->Close(fc);
- }
- fc = NULL;
- header.clear();
- body.clear();
- return 0;
- }
- default:
- return 0;
- }
- } else if (protocol == HTTP_V2) {
- return parser->GetSendData(data, len);
- }
- return 0;
- }
|