From ef26e7756032f78acb7c3a9c9c5add339daa8be9 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 4 Apr 2014 23:06:27 +0800 Subject: [PATCH] implements the http stream module. --- trunk/src/app/srs_app_http_conn.cpp | 638 +++++++++++++--------------- 1 file changed, 303 insertions(+), 335 deletions(-) mode change 100644 => 100755 trunk/src/app/srs_app_http_conn.cpp diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp old mode 100644 new mode 100755 index 58ee546cd..8d8c37b7f --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -1,335 +1,303 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2014 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#ifdef SRS_HTTP_SERVER - -#include -using namespace std; - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -SrsHttpRoot::SrsHttpRoot() -{ - // TODO: FIXME: support reload vhosts. -} - -SrsHttpRoot::~SrsHttpRoot() -{ -} - -int SrsHttpRoot::initialize() -{ - int ret = ERROR_SUCCESS; - - // add root - handlers.push_back(new SrsHttpVhost("__http__", "/", _srs_config->get_http_stream_dir())); - - // add other virtual path - SrsConfDirective* root = _srs_config->get_root(); - for (int i = 0; i < (int)root->directives.size(); i++) { - SrsConfDirective* conf = root->at(i); - - if (!conf->is_vhost()) { - continue; - } - - std::string vhost = conf->arg0(); - if (!_srs_config->get_vhost_http_enabled(vhost)) { - continue; - } - - std::string mount = _srs_config->get_vhost_http_mount(vhost); - std::string dir = _srs_config->get_vhost_http_dir(vhost); - - handlers.push_back(new SrsHttpVhost(vhost, mount, dir)); - } - - return ret; -} - -bool SrsHttpRoot::can_handle(const char* path, int length, const char** pchild) -{ - // reset the child path to path, - // for child to reparse the path. - *pchild = path; - - // only compare the first char. - return srs_path_equals("/", path, 1); -} - -bool SrsHttpRoot::is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase) -{ - if (!SrsHttpHandler::is_handler_valid(req, status_code, reason_phrase)) { - return false; - } - - if (req->match()->matched_url.length() != 1) { - status_code = HTTP_NotFound; - reason_phrase = HTTP_NotFound_str; - return false; - } - - return true; -} - -int SrsHttpRoot::do_process_request(SrsSocket* skt, SrsHttpMessage* req) -{ - std::stringstream ss; - - ss << JOBJECT_START - << JFIELD_ERROR(ERROR_SUCCESS) << JFIELD_CONT - << JFIELD_ORG("urls", JOBJECT_START); - - vector::iterator it; - for (it = handlers.begin(); it != handlers.end(); ++it) { - SrsHttpVhost* handler = dynamic_cast(*it); - srs_assert(handler); - - ss << JFIELD_ORG(handler->mount(), JOBJECT_START) - << JFIELD_STR("mount", handler->mount()) << JFIELD_CONT - << JFIELD_STR("vhost", handler->vhost()) << JFIELD_CONT - << JFIELD_STR("dir", handler->dir()) - << JOBJECT_END; - - if (it + 1 != handlers.end()) { - ss << JFIELD_CONT; - } - } - - ss << JOBJECT_END - << JOBJECT_END; - - return res_json(skt, req, ss.str()); -} - -SrsHttpVhost::SrsHttpVhost(std::string vhost, std::string mount, std::string dir) -{ - _vhost = vhost; - _mount = mount; - _dir = dir; -} - -SrsHttpVhost::~SrsHttpVhost() -{ -} - -bool SrsHttpVhost::can_handle(const char* path, int length, const char** /*pchild*/) -{ - int min_match = srs_min(length, (int)_mount.length()); - return srs_path_equals(_mount.c_str(), path, min_match); -} - -bool SrsHttpVhost::is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase) -{ - std::string fullpath = _dir + "/" + req->match()->unmatched_url; - if (req->match()->unmatched_url.empty()) { - fullpath += req->match()->matched_url; - } - - if (::access(fullpath.c_str(), F_OK | R_OK) < 0) { - srs_warn("check file %s does not exists", fullpath.c_str()); - - status_code = HTTP_NotFound; - reason_phrase = HTTP_NotFound_str; - return false; - } - - return true; -} - -int SrsHttpVhost::do_process_request(SrsSocket* skt, SrsHttpMessage* req) -{ - int ret = ERROR_SUCCESS; - - std::string fullpath = _dir + "/" + req->match()->unmatched_url; - if (req->match()->unmatched_url.empty()) { - fullpath += req->match()->matched_url; - } - - if (srs_string_ends_with(fullpath, "/")) { - fullpath += "index.html"; - } - - int fd = ::open(fullpath.c_str(), O_RDONLY); - if (fd < 0) { - ret = ERROR_HTTP_OPEN_FILE; - srs_warn("open file %s failed, ret=%d", fullpath.c_str(), ret); - return ret; - } - - int64_t length = (int64_t)::lseek(fd, 0, SEEK_END); - ::lseek(fd, 0, SEEK_SET); - - char* buf = new char[length]; - SrsAutoFree(char, buf, true); - - if (::read(fd, buf, length) < 0) { - ::close(fd); - ret = ERROR_HTTP_READ_FILE; - srs_warn("read file %s failed, ret=%d", fullpath.c_str(), ret); - return ret; - } - ::close(fd); - - std::string str; - str.append(buf, length); - - if (srs_string_ends_with(fullpath, ".ts")) { - return res_mpegts(skt, req, str); - } else if (srs_string_ends_with(fullpath, ".m3u8")) { - return res_m3u8(skt, req, str); - } else { - return res_text(skt, req, str); - } - - return ret; -} - -string SrsHttpVhost::vhost() -{ - return _vhost; -} - -string SrsHttpVhost::mount() -{ - return _mount; -} - -string SrsHttpVhost::dir() -{ - return _dir; -} - -SrsHttpConn::SrsHttpConn(SrsServer* srs_server, st_netfd_t client_stfd, SrsHttpHandler* _handler) - : SrsConnection(srs_server, client_stfd) -{ - parser = new SrsHttpParser(); - handler = _handler; - requires_crossdomain = false; -} - -SrsHttpConn::~SrsHttpConn() -{ - srs_freep(parser); -} - -int SrsHttpConn::do_cycle() -{ - int ret = ERROR_SUCCESS; - - if ((ret = get_peer_ip()) != ERROR_SUCCESS) { - srs_error("get peer ip failed. ret=%d", ret); - return ret; - } - srs_trace("http get peer ip success. ip=%s", ip); - - // initialize parser - if ((ret = parser->initialize(HTTP_REQUEST)) != ERROR_SUCCESS) { - srs_error("http initialize http parser failed. ret=%d", ret); - return ret; - } - - // underlayer socket - SrsSocket skt(stfd); - - // process http messages. - for (;;) { - SrsHttpMessage* req = NULL; - - // get a http message - if ((ret = parser->parse_message(&skt, &req)) != ERROR_SUCCESS) { - return ret; - } - - // if SUCCESS, always NOT-NULL and completed message. - srs_assert(req); - srs_assert(req->is_complete()); - - // always free it in this scope. - SrsAutoFree(SrsHttpMessage, req, false); - - // ok, handle http request. - if ((ret = process_request(&skt, req)) != ERROR_SUCCESS) { - return ret; - } - } - - return ret; -} - -int SrsHttpConn::process_request(SrsSocket* skt, SrsHttpMessage* req) -{ - int ret = ERROR_SUCCESS; - - // parse uri to schema/server:port/path?query - if ((ret = req->parse_uri()) != ERROR_SUCCESS) { - return ret; - } - - srs_trace("http request parsed, method=%d, url=%s, content-length=%"PRId64"", - req->method(), req->url().c_str(), req->content_length()); - - // TODO: maybe need to parse the url. - std::string url = req->path(); - - SrsHttpHandlerMatch* p = NULL; - if ((ret = handler->best_match(url.data(), url.length(), &p)) != ERROR_SUCCESS) { - srs_warn("failed to find the best match handler for url. ret=%d", ret); - return ret; - } - - // if success, p and pstart should be valid. - srs_assert(p); - srs_assert(p->handler); - srs_assert(p->matched_url.length() <= url.length()); - srs_info("best match handler, matched_url=%s", p->matched_url.c_str()); - - req->set_match(p); - req->set_requires_crossdomain(requires_crossdomain); - - // use handler to process request. - if ((ret = p->handler->process_request(skt, req)) != ERROR_SUCCESS) { - srs_warn("handler failed to process http request. ret=%d", ret); - return ret; - } - - if (req->requires_crossdomain()) { - requires_crossdomain = true; - } - - return ret; -} - -#endif +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#ifdef SRS_HTTP_SERVER + +#include +using namespace std; + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +SrsHttpRoot::SrsHttpRoot() +{ + // TODO: FIXME: support reload vhosts. +} + +SrsHttpRoot::~SrsHttpRoot() +{ +} + +int SrsHttpRoot::initialize() +{ + int ret = ERROR_SUCCESS; + + // add root + handlers.push_back(new SrsHttpVhost("__http__", "/", _srs_config->get_http_stream_dir())); + + // add other virtual path + SrsConfDirective* root = _srs_config->get_root(); + for (int i = 0; i < (int)root->directives.size(); i++) { + SrsConfDirective* conf = root->at(i); + + if (!conf->is_vhost()) { + continue; + } + + std::string vhost = conf->arg0(); + if (!_srs_config->get_vhost_http_enabled(vhost)) { + continue; + } + + std::string mount = _srs_config->get_vhost_http_mount(vhost); + std::string dir = _srs_config->get_vhost_http_dir(vhost); + + handlers.push_back(new SrsHttpVhost(vhost, mount, dir)); + } + + return ret; +} + +bool SrsHttpRoot::can_handle(const char* path, int length, const char** pchild) +{ + // reset the child path to path, + // for child to reparse the path. + *pchild = path; + + // never handle request for root. + return false; +} + +bool SrsHttpRoot::is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase) +{ + status_code = HTTP_InternalServerError; + reason_phrase = HTTP_InternalServerError_str; + return false; +} + +int SrsHttpRoot::do_process_request(SrsSocket* skt, SrsHttpMessage* req) +{ + int ret = ERROR_SUCCESS; + return ret; +} + +SrsHttpVhost::SrsHttpVhost(std::string vhost, std::string mount, std::string dir) +{ + _vhost = vhost; + _mount = mount; + _dir = dir; +} + +SrsHttpVhost::~SrsHttpVhost() +{ +} + +bool SrsHttpVhost::can_handle(const char* path, int length, const char** /*pchild*/) +{ + int min_match = srs_min(length, (int)_mount.length()); + return srs_path_equals(_mount.c_str(), path, min_match); +} + +bool SrsHttpVhost::is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase) +{ + std::string fullpath = _dir + "/" + req->match()->unmatched_url; + if (req->match()->unmatched_url.empty()) { + fullpath += req->match()->matched_url; + } + + if (::access(fullpath.c_str(), F_OK | R_OK) < 0) { + srs_warn("check file %s does not exists", fullpath.c_str()); + + status_code = HTTP_NotFound; + reason_phrase = HTTP_NotFound_str; + return false; + } + + return true; +} + +int SrsHttpVhost::do_process_request(SrsSocket* skt, SrsHttpMessage* req) +{ + int ret = ERROR_SUCCESS; + + std::string fullpath = _dir + "/" + req->match()->unmatched_url; + if (req->match()->unmatched_url.empty()) { + fullpath += req->match()->matched_url; + } + + if (srs_string_ends_with(fullpath, "/")) { + fullpath += "index.html"; + } + + int fd = ::open(fullpath.c_str(), O_RDONLY); + if (fd < 0) { + ret = ERROR_HTTP_OPEN_FILE; + srs_warn("open file %s failed, ret=%d", fullpath.c_str(), ret); + return ret; + } + + int64_t length = (int64_t)::lseek(fd, 0, SEEK_END); + ::lseek(fd, 0, SEEK_SET); + + char* buf = new char[length]; + SrsAutoFree(char, buf, true); + + if (::read(fd, buf, length) < 0) { + ::close(fd); + ret = ERROR_HTTP_READ_FILE; + srs_warn("read file %s failed, ret=%d", fullpath.c_str(), ret); + return ret; + } + ::close(fd); + + std::string str; + str.append(buf, length); + + if (srs_string_ends_with(fullpath, ".ts")) { + return res_mpegts(skt, req, str); + } else if (srs_string_ends_with(fullpath, ".m3u8")) { + return res_m3u8(skt, req, str); + } else { + return res_text(skt, req, str); + } + + return ret; +} + +string SrsHttpVhost::vhost() +{ + return _vhost; +} + +string SrsHttpVhost::mount() +{ + return _mount; +} + +string SrsHttpVhost::dir() +{ + return _dir; +} + +SrsHttpConn::SrsHttpConn(SrsServer* srs_server, st_netfd_t client_stfd, SrsHttpHandler* _handler) + : SrsConnection(srs_server, client_stfd) +{ + parser = new SrsHttpParser(); + handler = _handler; + requires_crossdomain = false; +} + +SrsHttpConn::~SrsHttpConn() +{ + srs_freep(parser); +} + +int SrsHttpConn::do_cycle() +{ + int ret = ERROR_SUCCESS; + + if ((ret = get_peer_ip()) != ERROR_SUCCESS) { + srs_error("get peer ip failed. ret=%d", ret); + return ret; + } + srs_trace("http get peer ip success. ip=%s", ip); + + // initialize parser + if ((ret = parser->initialize(HTTP_REQUEST)) != ERROR_SUCCESS) { + srs_error("http initialize http parser failed. ret=%d", ret); + return ret; + } + + // underlayer socket + SrsSocket skt(stfd); + + // process http messages. + for (;;) { + SrsHttpMessage* req = NULL; + + // get a http message + if ((ret = parser->parse_message(&skt, &req)) != ERROR_SUCCESS) { + return ret; + } + + // if SUCCESS, always NOT-NULL and completed message. + srs_assert(req); + srs_assert(req->is_complete()); + + // always free it in this scope. + SrsAutoFree(SrsHttpMessage, req, false); + + // ok, handle http request. + if ((ret = process_request(&skt, req)) != ERROR_SUCCESS) { + return ret; + } + } + + return ret; +} + +int SrsHttpConn::process_request(SrsSocket* skt, SrsHttpMessage* req) +{ + int ret = ERROR_SUCCESS; + + // parse uri to schema/server:port/path?query + if ((ret = req->parse_uri()) != ERROR_SUCCESS) { + return ret; + } + + srs_trace("http request parsed, method=%d, url=%s, content-length=%"PRId64"", + req->method(), req->url().c_str(), req->content_length()); + + // TODO: maybe need to parse the url. + std::string url = req->path(); + + SrsHttpHandlerMatch* p = NULL; + if ((ret = handler->best_match(url.data(), url.length(), &p)) != ERROR_SUCCESS) { + srs_warn("failed to find the best match handler for url. ret=%d", ret); + return ret; + } + + // if success, p and pstart should be valid. + srs_assert(p); + srs_assert(p->handler); + srs_assert(p->matched_url.length() <= url.length()); + srs_info("best match handler, matched_url=%s", p->matched_url.c_str()); + + req->set_match(p); + req->set_requires_crossdomain(requires_crossdomain); + + // use handler to process request. + if ((ret = p->handler->process_request(skt, req)) != ERROR_SUCCESS) { + srs_warn("handler failed to process http request. ret=%d", ret); + return ret; + } + + if (req->requires_crossdomain()) { + requires_crossdomain = true; + } + + return ret; +} + +#endif