diff --git a/trunk/src/app/srs_app_heartbeat.cpp b/trunk/src/app/srs_app_heartbeat.cpp index ecb9b9196..fa59e1a87 100644 --- a/trunk/src/app/srs_app_heartbeat.cpp +++ b/trunk/src/app/srs_app_heartbeat.cpp @@ -82,14 +82,14 @@ void SrsHttpHeartbeat::heartbeat() return; } - SrsHttpMessage* msg = NULL; + ISrsHttpMessage* msg = NULL; if ((ret = http.post(uri.get_path(), req, &msg)) != ERROR_SUCCESS) { srs_info("http post hartbeart uri failed. " "url=%s, request=%s, response=%s, ret=%d", url.c_str(), req.c_str(), res.c_str(), ret); return; } - SrsAutoFree(SrsHttpMessage, msg); + SrsAutoFree(ISrsHttpMessage, msg); std::string res; if ((ret = msg->body_read_all(res)) != ERROR_SUCCESS) { diff --git a/trunk/src/app/srs_app_http.cpp b/trunk/src/app/srs_app_http.cpp index a343f5535..d5d367835 100644 --- a/trunk/src/app/srs_app_http.cpp +++ b/trunk/src/app/srs_app_http.cpp @@ -25,726 +25,17 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifdef SRS_AUTO_HTTP_PARSER -#include -#include -#include +#include using namespace std; #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include #include - -#define SRS_DEFAULT_HTTP_PORT 80 - -// for http parser macros -#define SRS_CONSTS_HTTP_OPTIONS HTTP_OPTIONS -#define SRS_CONSTS_HTTP_GET HTTP_GET -#define SRS_CONSTS_HTTP_POST HTTP_POST -#define SRS_CONSTS_HTTP_PUT HTTP_PUT -#define SRS_CONSTS_HTTP_DELETE HTTP_DELETE - -// for ead all of http body, read each time. -#define SRS_HTTP_READ_CACHE_BYTES 4096 - -#define SRS_HTTP_DEFAULT_PAGE "index.html" - -int srs_http_response_json(ISrsHttpResponseWriter* w, string data) -{ - w->header()->set_content_length(data.length()); - w->header()->set_content_type("application/json"); - - return w->write((char*)data.data(), (int)data.length()); -} - -// get the status text of code. -string srs_generate_http_status_text(int status) -{ - static std::map _status_map; - if (_status_map.empty()) { - _status_map[SRS_CONSTS_HTTP_Continue ] = SRS_CONSTS_HTTP_Continue_str ; - _status_map[SRS_CONSTS_HTTP_SwitchingProtocols ] = SRS_CONSTS_HTTP_SwitchingProtocols_str ; - _status_map[SRS_CONSTS_HTTP_OK ] = SRS_CONSTS_HTTP_OK_str ; - _status_map[SRS_CONSTS_HTTP_Created ] = SRS_CONSTS_HTTP_Created_str ; - _status_map[SRS_CONSTS_HTTP_Accepted ] = SRS_CONSTS_HTTP_Accepted_str ; - _status_map[SRS_CONSTS_HTTP_NonAuthoritativeInformation ] = SRS_CONSTS_HTTP_NonAuthoritativeInformation_str ; - _status_map[SRS_CONSTS_HTTP_NoContent ] = SRS_CONSTS_HTTP_NoContent_str ; - _status_map[SRS_CONSTS_HTTP_ResetContent ] = SRS_CONSTS_HTTP_ResetContent_str ; - _status_map[SRS_CONSTS_HTTP_PartialContent ] = SRS_CONSTS_HTTP_PartialContent_str ; - _status_map[SRS_CONSTS_HTTP_MultipleChoices ] = SRS_CONSTS_HTTP_MultipleChoices_str ; - _status_map[SRS_CONSTS_HTTP_MovedPermanently ] = SRS_CONSTS_HTTP_MovedPermanently_str ; - _status_map[SRS_CONSTS_HTTP_Found ] = SRS_CONSTS_HTTP_Found_str ; - _status_map[SRS_CONSTS_HTTP_SeeOther ] = SRS_CONSTS_HTTP_SeeOther_str ; - _status_map[SRS_CONSTS_HTTP_NotModified ] = SRS_CONSTS_HTTP_NotModified_str ; - _status_map[SRS_CONSTS_HTTP_UseProxy ] = SRS_CONSTS_HTTP_UseProxy_str ; - _status_map[SRS_CONSTS_HTTP_TemporaryRedirect ] = SRS_CONSTS_HTTP_TemporaryRedirect_str ; - _status_map[SRS_CONSTS_HTTP_BadRequest ] = SRS_CONSTS_HTTP_BadRequest_str ; - _status_map[SRS_CONSTS_HTTP_Unauthorized ] = SRS_CONSTS_HTTP_Unauthorized_str ; - _status_map[SRS_CONSTS_HTTP_PaymentRequired ] = SRS_CONSTS_HTTP_PaymentRequired_str ; - _status_map[SRS_CONSTS_HTTP_Forbidden ] = SRS_CONSTS_HTTP_Forbidden_str ; - _status_map[SRS_CONSTS_HTTP_NotFound ] = SRS_CONSTS_HTTP_NotFound_str ; - _status_map[SRS_CONSTS_HTTP_MethodNotAllowed ] = SRS_CONSTS_HTTP_MethodNotAllowed_str ; - _status_map[SRS_CONSTS_HTTP_NotAcceptable ] = SRS_CONSTS_HTTP_NotAcceptable_str ; - _status_map[SRS_CONSTS_HTTP_ProxyAuthenticationRequired ] = SRS_CONSTS_HTTP_ProxyAuthenticationRequired_str ; - _status_map[SRS_CONSTS_HTTP_RequestTimeout ] = SRS_CONSTS_HTTP_RequestTimeout_str ; - _status_map[SRS_CONSTS_HTTP_Conflict ] = SRS_CONSTS_HTTP_Conflict_str ; - _status_map[SRS_CONSTS_HTTP_Gone ] = SRS_CONSTS_HTTP_Gone_str ; - _status_map[SRS_CONSTS_HTTP_LengthRequired ] = SRS_CONSTS_HTTP_LengthRequired_str ; - _status_map[SRS_CONSTS_HTTP_PreconditionFailed ] = SRS_CONSTS_HTTP_PreconditionFailed_str ; - _status_map[SRS_CONSTS_HTTP_RequestEntityTooLarge ] = SRS_CONSTS_HTTP_RequestEntityTooLarge_str ; - _status_map[SRS_CONSTS_HTTP_RequestURITooLarge ] = SRS_CONSTS_HTTP_RequestURITooLarge_str ; - _status_map[SRS_CONSTS_HTTP_UnsupportedMediaType ] = SRS_CONSTS_HTTP_UnsupportedMediaType_str ; - _status_map[SRS_CONSTS_HTTP_RequestedRangeNotSatisfiable ] = SRS_CONSTS_HTTP_RequestedRangeNotSatisfiable_str ; - _status_map[SRS_CONSTS_HTTP_ExpectationFailed ] = SRS_CONSTS_HTTP_ExpectationFailed_str ; - _status_map[SRS_CONSTS_HTTP_InternalServerError ] = SRS_CONSTS_HTTP_InternalServerError_str ; - _status_map[SRS_CONSTS_HTTP_NotImplemented ] = SRS_CONSTS_HTTP_NotImplemented_str ; - _status_map[SRS_CONSTS_HTTP_BadGateway ] = SRS_CONSTS_HTTP_BadGateway_str ; - _status_map[SRS_CONSTS_HTTP_ServiceUnavailable ] = SRS_CONSTS_HTTP_ServiceUnavailable_str ; - _status_map[SRS_CONSTS_HTTP_GatewayTimeout ] = SRS_CONSTS_HTTP_GatewayTimeout_str ; - _status_map[SRS_CONSTS_HTTP_HTTPVersionNotSupported ] = SRS_CONSTS_HTTP_HTTPVersionNotSupported_str ; - } - - std::string status_text; - if (_status_map.find(status) == _status_map.end()) { - status_text = "Status Unknown"; - } else { - status_text = _status_map[status]; - } - - return status_text; -} - -// bodyAllowedForStatus reports whether a given response status code -// permits a body. See RFC2616, section 4.4. -bool srs_go_http_body_allowd(int status) -{ - if (status >= 100 && status <= 199) { - return false; - } else if (status == 204 || status == 304) { - return false; - } - - return true; -} - -// DetectContentType implements the algorithm described -// at http://mimesniff.spec.whatwg.org/ to determine the -// Content-Type of the given data. It considers at most the -// first 512 bytes of data. DetectContentType always returns -// a valid MIME type: if it cannot determine a more specific one, it -// returns "application/octet-stream". -string srs_go_http_detect(char* data, int size) -{ - // detect only when data specified. - if (data) { - } - return "application/octet-stream"; // fallback -} - -// Error replies to the request with the specified error message and HTTP code. -// The error message should be plain text. -int srs_go_http_error(ISrsHttpResponseWriter* w, int code, string error) -{ - int ret = ERROR_SUCCESS; - - w->header()->set_content_type("text/plain; charset=utf-8"); - w->header()->set_content_length(error.length()); - w->write_header(code); - w->write((char*)error.data(), (int)error.length()); - - return ret; -} - -SrsHttpHeader::SrsHttpHeader() -{ -} - -SrsHttpHeader::~SrsHttpHeader() -{ -} - -void SrsHttpHeader::set(string key, string value) -{ - headers[key] = value; -} - -string SrsHttpHeader::get(string key) -{ - std::string v; - - if (headers.find(key) != headers.end()) { - v = headers[key]; - } - - return v; -} - -int64_t SrsHttpHeader::content_length() -{ - std::string cl = get("Content-Length"); - - if (cl.empty()) { - return -1; - } - - return (int64_t)::atof(cl.c_str()); -} - -void SrsHttpHeader::set_content_length(int64_t size) -{ - char buf[64]; - snprintf(buf, sizeof(buf), "%"PRId64, size); - set("Content-Length", buf); -} - -string SrsHttpHeader::content_type() -{ - return get("Content-Type"); -} - -void SrsHttpHeader::set_content_type(string ct) -{ - set("Content-Type", ct); -} - -void SrsHttpHeader::write(stringstream& ss) -{ - std::map::iterator it; - for (it = headers.begin(); it != headers.end(); ++it) { - ss << it->first << ": " << it->second << SRS_HTTP_CRLF; - } -} - -ISrsHttpResponseWriter::ISrsHttpResponseWriter() -{ -} - -ISrsHttpResponseWriter::~ISrsHttpResponseWriter() -{ -} - -ISrsHttpResponseReader::ISrsHttpResponseReader() -{ -} - -ISrsHttpResponseReader::~ISrsHttpResponseReader() -{ -} - -ISrsHttpHandler::ISrsHttpHandler() -{ - entry = NULL; -} - -ISrsHttpHandler::~ISrsHttpHandler() -{ -} - -SrsHttpRedirectHandler::SrsHttpRedirectHandler(string u, int c) -{ - url = u; - code = c; -} - -SrsHttpRedirectHandler::~SrsHttpRedirectHandler() -{ -} - -int SrsHttpRedirectHandler::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) -{ - int ret = ERROR_SUCCESS; - // TODO: FIXME: implements it. - return ret; -} - -SrsHttpNotFoundHandler::SrsHttpNotFoundHandler() -{ -} - -SrsHttpNotFoundHandler::~SrsHttpNotFoundHandler() -{ -} - -int SrsHttpNotFoundHandler::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) -{ - return srs_go_http_error(w, - SRS_CONSTS_HTTP_NotFound, SRS_CONSTS_HTTP_NotFound_str); -} - -SrsHttpFileServer::SrsHttpFileServer(string root_dir) -{ - dir = root_dir; -} - -SrsHttpFileServer::~SrsHttpFileServer() -{ -} - -int SrsHttpFileServer::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) -{ - string upath = r->path(); - - // add default pages. - if (srs_string_ends_with(upath, "/")) { - upath += SRS_HTTP_DEFAULT_PAGE; - } - - string fullpath = dir + "/"; - - // remove the virtual directory. - srs_assert(entry); - size_t pos = entry->pattern.find("/"); - if (upath.length() > entry->pattern.length() && pos != string::npos) { - fullpath += upath.substr(entry->pattern.length() - pos); - } else { - fullpath += upath; - } - - // stat current dir, if exists, return error. - if (!srs_path_exists(fullpath)) { - srs_warn("http miss file=%s, pattern=%s, upath=%s", - fullpath.c_str(), entry->pattern.c_str(), upath.c_str()); - return SrsHttpNotFoundHandler().serve_http(w, r); - } - srs_trace("http match file=%s, pattern=%s, upath=%s", - fullpath.c_str(), entry->pattern.c_str(), upath.c_str()); - - // handle file according to its extension. - // use vod stream for .flv/.fhv - if (srs_string_ends_with(fullpath, ".flv") || srs_string_ends_with(fullpath, ".fhv")) { - return serve_flv_file(w, r, fullpath); - } else if (srs_string_ends_with(fullpath, ".mp4")) { - return serve_mp4_file(w, r, fullpath); - } - - // serve common static file. - return serve_file(w, r, fullpath); -} - -int SrsHttpFileServer::serve_file(ISrsHttpResponseWriter* w, SrsHttpMessage* r, string fullpath) -{ - int ret = ERROR_SUCCESS; - - // open the target file. - SrsFileReader fs; - - if ((ret = fs.open(fullpath)) != ERROR_SUCCESS) { - srs_warn("open file %s failed, ret=%d", fullpath.c_str(), ret); - return ret; - } - - int64_t length = fs.filesize(); - - // unset the content length to encode in chunked encoding. - w->header()->set_content_length(length); - - static std::map _mime; - if (_mime.empty()) { - _mime[".ts"] = "video/MP2T"; - _mime[".flv"] = "video/x-flv"; - _mime[".m4v"] = "video/x-m4v"; - _mime[".3gpp"] = "video/3gpp"; - _mime[".3gp"] = "video/3gpp"; - _mime[".mp4"] = "video/mp4"; - _mime[".aac"] = "audio/x-aac"; - _mime[".mp3"] = "audio/mpeg"; - _mime[".m4a"] = "audio/x-m4a"; - _mime[".ogg"] = "audio/ogg"; - // @see hls-m3u8-draft-pantos-http-live-streaming-12.pdf, page 5. - _mime[".m3u8"] = "application/vnd.apple.mpegurl"; // application/x-mpegURL - _mime[".rss"] = "application/rss+xml"; - _mime[".json"] = "application/json"; - _mime[".swf"] = "application/x-shockwave-flash"; - _mime[".doc"] = "application/msword"; - _mime[".zip"] = "application/zip"; - _mime[".rar"] = "application/x-rar-compressed"; - _mime[".xml"] = "text/xml"; - _mime[".html"] = "text/html"; - _mime[".js"] = "text/javascript"; - _mime[".css"] = "text/css"; - _mime[".ico"] = "image/x-icon"; - _mime[".png"] = "image/png"; - _mime[".jpeg"] = "image/jpeg"; - _mime[".jpg"] = "image/jpeg"; - _mime[".gif"] = "image/gif"; - } - - if (true) { - size_t pos; - std::string ext = fullpath; - if ((pos = ext.rfind(".")) != string::npos) { - ext = ext.substr(pos); - } - - if (_mime.find(ext) == _mime.end()) { - w->header()->set_content_type("application/octet-stream"); - } else { - w->header()->set_content_type(_mime[ext]); - } - } - - // write body. - int64_t left = length; - if ((ret = copy(w, &fs, r, (int)left)) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("read file=%s size=%d failed, ret=%d", fullpath.c_str(), left, ret); - } - return ret; - } - - return w->final_request(); -} - -int SrsHttpFileServer::serve_flv_file(ISrsHttpResponseWriter* w, SrsHttpMessage* r, string fullpath) -{ - std::string start = r->query_get("start"); - if (start.empty()) { - return serve_file(w, r, fullpath); - } - - int offset = ::atoi(start.c_str()); - if (offset <= 0) { - return serve_file(w, r, fullpath); - } - - return serve_flv_stream(w, r, fullpath, offset); -} - -int SrsHttpFileServer::serve_mp4_file(ISrsHttpResponseWriter* w, SrsHttpMessage* r, string fullpath) -{ - // for flash to request mp4 range in query string. - // for example, http://digitalprimates.net/dash/DashTest.html?url=http://dashdemo.edgesuite.net/digitalprimates/nexus/oops-20120802-manifest.mpd - std::string range = r->query_get("range"); - // or, use bytes to request range, - // for example, http://dashas.castlabs.com/demo/try.html - if (range.empty()) { - range = r->query_get("bytes"); - } - - // rollback to serve whole file. - size_t pos = string::npos; - if (range.empty() || (pos = range.find("-")) == string::npos) { - return serve_file(w, r, fullpath); - } - - // parse the start in query string - int start = 0; - if (pos > 0) { - start = ::atoi(range.substr(0, pos).c_str()); - } - - // parse end in query string. - int end = -1; - if (pos < range.length() - 1) { - end = ::atoi(range.substr(pos + 1).c_str()); - } - - // invalid param, serve as whole mp4 file. - if (start < 0 || (end != -1 && start > end)) { - return serve_file(w, r, fullpath); - } - - return serve_mp4_stream(w, r, fullpath, start, end); -} - -int SrsHttpFileServer::serve_flv_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, string fullpath, int offset) -{ - return serve_file(w, r, fullpath); -} - -int SrsHttpFileServer::serve_mp4_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, string fullpath, int start, int end) -{ - return serve_file(w, r, fullpath); -} - -int SrsHttpFileServer::copy(ISrsHttpResponseWriter* w, SrsFileReader* fs, SrsHttpMessage* r, int size) -{ - int ret = ERROR_SUCCESS; - - int left = size; - char* buf = r->http_ts_send_buffer(); - - while (left > 0) { - ssize_t nread = -1; - int max_read = srs_min(left, SRS_HTTP_TS_SEND_BUFFER_SIZE); - if ((ret = fs->read(buf, max_read, &nread)) != ERROR_SUCCESS) { - break; - } - - left -= nread; - if ((ret = w->write(buf, (int)nread)) != ERROR_SUCCESS) { - break; - } - } - - return ret; -} - -SrsHttpMuxEntry::SrsHttpMuxEntry() -{ - enabled = true; - explicit_match = false; - handler = NULL; -} - -SrsHttpMuxEntry::~SrsHttpMuxEntry() -{ - srs_freep(handler); -} - -ISrsHttpMatchHijacker::ISrsHttpMatchHijacker() -{ -} - -ISrsHttpMatchHijacker::~ISrsHttpMatchHijacker() -{ -} - -SrsHttpServeMux::SrsHttpServeMux() -{ -} - -SrsHttpServeMux::~SrsHttpServeMux() -{ - std::map::iterator it; - for (it = entries.begin(); it != entries.end(); ++it) { - SrsHttpMuxEntry* entry = it->second; - srs_freep(entry); - } - entries.clear(); - - vhosts.clear(); - hijackers.clear(); -} - -int SrsHttpServeMux::initialize() -{ - int ret = ERROR_SUCCESS; - // TODO: FIXME: implements it. - return ret; -} - -void SrsHttpServeMux::hijack(ISrsHttpMatchHijacker* h) -{ - std::vector::iterator it = ::find(hijackers.begin(), hijackers.end(), h); - if (it != hijackers.end()) { - return; - } - hijackers.push_back(h); -} - -void SrsHttpServeMux::unhijack(ISrsHttpMatchHijacker* h) -{ - std::vector::iterator it = ::find(hijackers.begin(), hijackers.end(), h); - if (it == hijackers.end()) { - return; - } - hijackers.erase(it); -} - -int SrsHttpServeMux::handle(std::string pattern, ISrsHttpHandler* handler) -{ - int ret = ERROR_SUCCESS; - - srs_assert(handler); - - if (pattern.empty()) { - ret = ERROR_HTTP_PATTERN_EMPTY; - srs_error("http: empty pattern. ret=%d", ret); - return ret; - } - - if (entries.find(pattern) != entries.end()) { - SrsHttpMuxEntry* exists = entries[pattern]; - if (exists->explicit_match) { - ret = ERROR_HTTP_PATTERN_DUPLICATED; - srs_error("http: multiple registrations for %s. ret=%d", pattern.c_str(), ret); - return ret; - } - } - - std::string vhost = pattern; - if (pattern.at(0) != '/') { - if (pattern.find("/") != string::npos) { - vhost = pattern.substr(0, pattern.find("/")); - } - vhosts[vhost] = handler; - } - - if (true) { - SrsHttpMuxEntry* entry = new SrsHttpMuxEntry(); - entry->explicit_match = true; - entry->handler = handler; - entry->pattern = pattern; - entry->handler->entry = entry; - - if (entries.find(pattern) != entries.end()) { - SrsHttpMuxEntry* exists = entries[pattern]; - srs_freep(exists); - } - entries[pattern] = entry; - } - - // Helpful behavior: - // If pattern is /tree/, insert an implicit permanent redirect for /tree. - // It can be overridden by an explicit registration. - if (pattern != "/" && !pattern.empty() && pattern.at(pattern.length() - 1) == '/') { - std::string rpattern = pattern.substr(0, pattern.length() - 1); - SrsHttpMuxEntry* entry = NULL; - - // free the exists not explicit entry - if (entries.find(rpattern) != entries.end()) { - SrsHttpMuxEntry* exists = entries[rpattern]; - if (!exists->explicit_match) { - entry = exists; - } - } - - // create implicit redirect. - if (!entry || entry->explicit_match) { - srs_freep(entry); - - entry = new SrsHttpMuxEntry(); - entry->explicit_match = false; - entry->handler = new SrsHttpRedirectHandler(pattern, SRS_CONSTS_HTTP_MovedPermanently); - entry->pattern = pattern; - entry->handler->entry = entry; - - entries[rpattern] = entry; - } - } - - return ret; -} - -int SrsHttpServeMux::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) -{ - int ret = ERROR_SUCCESS; - - ISrsHttpHandler* h = NULL; - if ((ret = find_handler(r, &h)) != ERROR_SUCCESS) { - srs_error("find handler failed. ret=%d", ret); - return ret; - } - - srs_assert(h); - if ((ret = h->serve_http(w, r)) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("handler serve http failed. ret=%d", ret); - } - return ret; - } - - return ret; -} - -int SrsHttpServeMux::find_handler(SrsHttpMessage* r, ISrsHttpHandler** ph) -{ - int ret = ERROR_SUCCESS; - - // TODO: FIXME: support the path . and .. - if (r->url().find("..") != std::string::npos) { - ret = ERROR_HTTP_URL_NOT_CLEAN; - srs_error("htt url not canonical, url=%s. ret=%d", r->url().c_str(), ret); - return ret; - } - - if ((ret = match(r, ph)) != ERROR_SUCCESS) { - srs_error("http match handler failed. ret=%d", ret); - return ret; - } - - // always hijack. - if (!hijackers.empty()) { - // notice all hijacker the match failed. - std::vector::iterator it; - for (it = hijackers.begin(); it != hijackers.end(); ++it) { - ISrsHttpMatchHijacker* hijacker = *it; - if ((ret = hijacker->hijack(r, ph)) != ERROR_SUCCESS) { - srs_error("hijacker match failed. ret=%d", ret); - return ret; - } - } - } - - if (*ph == NULL) { - // TODO: FIXME: memory leak. - *ph = new SrsHttpNotFoundHandler(); - } - - return ret; -} - -int SrsHttpServeMux::match(SrsHttpMessage* r, ISrsHttpHandler** ph) -{ - int ret = ERROR_SUCCESS; - - std::string path = r->path(); - - // Host-specific pattern takes precedence over generic ones - if (!vhosts.empty() && vhosts.find(r->host()) != vhosts.end()) { - path = r->host() + path; - } - - int nb_matched = 0; - ISrsHttpHandler* h = NULL; - - std::map::iterator it; - for (it = entries.begin(); it != entries.end(); ++it) { - std::string pattern = it->first; - SrsHttpMuxEntry* entry = it->second; - - if (!entry->enabled) { - continue; - } - - if (!path_match(pattern, path)) { - continue; - } - - if (!h || (int)pattern.length() > nb_matched) { - nb_matched = (int)pattern.length(); - h = entry->handler; - } - } - - *ph = h; - - return ret; -} - -bool SrsHttpServeMux::path_match(string pattern, string path) -{ - if (pattern.empty()) { - return false; - } - - int n = (int)pattern.length(); - - // not endswith '/', exactly match. - if (pattern.at(n - 1) != '/') { - return pattern == path; - } - - // endswith '/', match any, - // for example, '/api/' match '/api/[N]' - if ((int)path.length() >= n) { - if (memcmp(pattern.data(), path.data(), n) == 0) { - return true; - } - } - - return false; -} +#include SrsHttpResponseWriter::SrsHttpResponseWriter(SrsStSocket* io) { @@ -800,7 +91,7 @@ int SrsHttpResponseWriter::write(char* data, int size) srs_error("http: send header failed. ret=%d", ret); return ret; } - + // ignore NULL content. if (!data) { return ret; @@ -854,9 +145,9 @@ int SrsHttpResponseWriter::send_header(char* data, int size) std::stringstream ss; // status_line - ss << "HTTP/1.1 " << status << " " - << srs_generate_http_status_text(status) << SRS_HTTP_CRLF; - + ss << "HTTP/1.1 " << status << " " + << srs_generate_http_status_text(status) << SRS_HTTP_CRLF; + // detect content type if (srs_go_http_body_allowd(status)) { if (hdr->content_type().empty()) { @@ -873,7 +164,7 @@ int SrsHttpResponseWriter::send_header(char* data, int size) if (content_length == -1) { hdr->set("Transfer-Encoding", "chunked"); } - + // keep alive to make vlc happy. hdr->set("Connection", "Keep-Alive"); @@ -1036,7 +327,7 @@ int SrsHttpResponseReader::read_chunked(char* data, int nb_data, int* nb_read) return ret; } buffer->read_slice(2); - + return ret; } @@ -1078,7 +369,7 @@ int SrsHttpResponseReader::read_specified(char* data, int nb_data, int* nb_read) return ret; } -SrsHttpMessage::SrsHttpMessage(SrsStSocket* io, SrsConnection* c) +SrsHttpMessage::SrsHttpMessage(SrsStSocket* io, SrsConnection* c) : ISrsHttpMessage() { conn = c; chunked = false; @@ -1098,11 +389,11 @@ SrsHttpMessage::~SrsHttpMessage() int SrsHttpMessage::update(string url, http_parser* header, SrsFastBuffer* body, vector& headers) { int ret = ERROR_SUCCESS; - + _url = url; _header = *header; _headers = headers; - + // whether chunked. std::string transfer_encoding = get_request_header("Transfer-Encoding"); chunked = (transfer_encoding == "chunked"); @@ -1117,8 +408,8 @@ int SrsHttpMessage::update(string url, http_parser* header, SrsFastBuffer* body, // parse uri from url. std::string host = get_request_header("Host"); - - // donot parse the empty host for uri, + + // donot parse the empty host for uri, // for example, the response contains no host, // ignore it is ok. if (host.empty()) { @@ -1165,11 +456,6 @@ int SrsHttpMessage::update(string url, http_parser* header, SrsFastBuffer* body, return ret; } -char* SrsHttpMessage::http_ts_send_buffer() -{ - return _http_ts_send_buffer; -} - SrsConnection* SrsHttpMessage::connection() { return conn; @@ -1409,7 +695,7 @@ int SrsHttpParser::initialize(enum http_parser_type type) return ret; } -int SrsHttpParser::parse_message(SrsStSocket* skt, SrsConnection* conn, SrsHttpMessage** ppmsg) +int SrsHttpParser::parse_message(SrsStSocket* skt, SrsConnection* conn, ISrsHttpMessage** ppmsg) { *ppmsg = NULL; diff --git a/trunk/src/app/srs_app_http.hpp b/trunk/src/app/srs_app_http.hpp index 685ad3668..7b1f9b3e4 100644 --- a/trunk/src/app/srs_app_http.hpp +++ b/trunk/src/app/srs_app_http.hpp @@ -34,366 +34,22 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include -#include #include #include -#include #include class SrsRequest; class SrsStSocket; -class SrsHttpUri; class SrsHttpMessage; -class SrsFileReader; -class SrsSimpleBuffer; -class SrsHttpMuxEntry; -class ISrsHttpResponseWriter; class SrsFastBuffer; +class SrsHttpUri; class SrsConnection; -// http specification -// CR = -#define SRS_HTTP_CR SRS_CONSTS_CR // 0x0D -// LF = -#define SRS_HTTP_LF SRS_CONSTS_LF // 0x0A -// SP = -#define SRS_HTTP_SP ' ' // 0x20 -// HT = -#define SRS_HTTP_HT '\x09' // 0x09 - -// HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all -// protocol elements except the entity-body (see appendix 19.3 for -// tolerant applications). -#define SRS_HTTP_CRLF "\r\n" // 0x0D0A -#define SRS_HTTP_CRLFCRLF "\r\n\r\n" // 0x0D0A0D0A - -// @see SrsHttpMessage._http_ts_send_buffer -#define SRS_HTTP_TS_SEND_BUFFER_SIZE 4096 - -// helper function: response in json format. -extern int srs_http_response_json(ISrsHttpResponseWriter* w, std::string data); - -// state of message -enum SrsHttpParseState { - SrsHttpParseStateInit = 0, - SrsHttpParseStateStart, - SrsHttpParseStateHeaderComplete, - SrsHttpParseStateMessageComplete -}; - -// A Header represents the key-value pairs in an HTTP header. -class SrsHttpHeader -{ -private: - std::map headers; -public: - SrsHttpHeader(); - virtual ~SrsHttpHeader(); -public: - // Add adds the key, value pair to the header. - // It appends to any existing values associated with key. - virtual void set(std::string key, std::string value); - // Get gets the first value associated with the given key. - // If there are no values associated with the key, Get returns "". - // To access multiple values of a key, access the map directly - // with CanonicalHeaderKey. - virtual std::string get(std::string key); -public: - /** - * get the content length. -1 if not set. - */ - virtual int64_t content_length(); - /** - * set the content length by header "Content-Length" - */ - virtual void set_content_length(int64_t size); -public: - /** - * get the content type. empty string if not set. - */ - virtual std::string content_type(); - /** - * set the content type by header "Content-Type" - */ - virtual void set_content_type(std::string ct); -public: - /** - * write all headers to string stream. - */ - virtual void write(std::stringstream& ss); -}; - -// A ResponseWriter interface is used by an HTTP handler to -// construct an HTTP response. -// Usage 1, response with specified length content: -// ISrsHttpResponseWriter* w; // create or get response. -// std::string msg = "Hello, HTTP!"; -// w->header()->set_content_type("text/plain; charset=utf-8"); -// w->header()->set_content_length(msg.length()); -// w->write_header(SRS_CONSTS_HTTP_OK); -// w->write((char*)msg.data(), (int)msg.length()); -// w->final_request(); // optional flush. -// Usage 2, response with HTTP code only, zero content length. -// ISrsHttpResponseWriter* w; // create or get response. -// w->header()->set_content_length(0); -// w->write_header(SRS_CONSTS_HTTP_OK); -// w->final_request(); -// Usage 3, response in chunked encoding. -// ISrsHttpResponseWriter* w; // create or get response. -// std::string msg = "Hello, HTTP!"; -// w->header()->set_content_type("application/octet-stream"); -// w->write_header(SRS_CONSTS_HTTP_OK); -// w->write((char*)msg.data(), (int)msg.length()); -// w->write((char*)msg.data(), (int)msg.length()); -// w->write((char*)msg.data(), (int)msg.length()); -// w->write((char*)msg.data(), (int)msg.length()); -// w->final_request(); // required to end the chunked and flush. -class ISrsHttpResponseWriter -{ -public: - ISrsHttpResponseWriter(); - virtual ~ISrsHttpResponseWriter(); -public: - // when chunked mode, - // final the request to complete the chunked encoding. - // for no-chunked mode, - // final to send request, for example, content-length is 0. - virtual int final_request() = 0; - - // Header returns the header map that will be sent by WriteHeader. - // Changing the header after a call to WriteHeader (or Write) has - // no effect. - virtual SrsHttpHeader* header() = 0; - - // Write writes the data to the connection as part of an HTTP reply. - // If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK) - // before writing the data. If the Header does not contain a - // Content-Type line, Write adds a Content-Type set to the result of passing - // the initial 512 bytes of written data to DetectContentType. - // @param data, the data to send. NULL to flush header only. - virtual int write(char* data, int size) = 0; - - // WriteHeader sends an HTTP response header with status code. - // If WriteHeader is not called explicitly, the first call to Write - // will trigger an implicit WriteHeader(http.StatusOK). - // Thus explicit calls to WriteHeader are mainly used to - // send error codes. - // @remark, user must set header then write or write_header. - virtual void write_header(int code) = 0; -}; - /** -* the reader interface for http response. -*/ -class ISrsHttpResponseReader -{ -public: - ISrsHttpResponseReader(); - virtual ~ISrsHttpResponseReader(); -public: - /** - * whether response read EOF. - */ - virtual bool eof() = 0; - /** - * read from the response body. - * @param data, the buffer to read data buffer to. - * @param nb_data, the max size of data buffer. - * @param nb_read, the actual read size of bytes. NULL to ignore. - * @remark when eof(), return error. - */ - virtual int read(char* data, int nb_data, int* nb_read) = 0; -}; - -// Objects implementing the Handler interface can be -// registered to serve a particular path or subtree -// in the HTTP server. -// -// ServeHTTP should write reply headers and data to the ResponseWriter -// and then return. Returning signals that the request is finished -// and that the HTTP server can move on to the next request on -// the connection. -class ISrsHttpHandler -{ -public: - SrsHttpMuxEntry* entry; -public: - ISrsHttpHandler(); - virtual ~ISrsHttpHandler(); -public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) = 0; -}; - -// Redirect to a fixed URL -class SrsHttpRedirectHandler : public ISrsHttpHandler -{ -private: - std::string url; - int code; -public: - SrsHttpRedirectHandler(std::string u, int c); - virtual ~SrsHttpRedirectHandler(); -public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); -}; - -// NotFound replies to the request with an HTTP 404 not found error. -class SrsHttpNotFoundHandler : public ISrsHttpHandler -{ -public: - SrsHttpNotFoundHandler(); - virtual ~SrsHttpNotFoundHandler(); -public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); -}; - -// FileServer returns a handler that serves HTTP requests -// with the contents of the file system rooted at root. -// -// To use the operating system's file system implementation, -// use http.Dir: -// -// http.Handle("/", SrsHttpFileServer("/tmp")) -// http.Handle("/", SrsHttpFileServer("static-dir")) -class SrsHttpFileServer : public ISrsHttpHandler -{ -protected: - std::string dir; -public: - SrsHttpFileServer(std::string root_dir); - virtual ~SrsHttpFileServer(); -public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); -private: - /** - * serve the file by specified path - */ - virtual int serve_file(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath); - virtual int serve_flv_file(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath); - virtual int serve_mp4_file(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath); -protected: - /** - * when access flv file with x.flv?start=xxx - */ - virtual int serve_flv_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int offset); - /** - * when access mp4 file with x.mp4?range=start-end - * @param start the start offset in bytes. - * @param end the end offset in bytes. -1 to end of file. - * @remark response data in [start, end]. - */ - virtual int serve_mp4_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int start, int end); -protected: - /** - * copy the fs to response writer in size bytes. - */ - virtual int copy(ISrsHttpResponseWriter* w, SrsFileReader* fs, SrsHttpMessage* r, int size); -}; - -// the mux entry for server mux. -// the matcher info, for example, the pattern and handler. -class SrsHttpMuxEntry -{ -public: - bool explicit_match; - ISrsHttpHandler* handler; - std::string pattern; - bool enabled; -public: - SrsHttpMuxEntry(); - virtual ~SrsHttpMuxEntry(); -}; - -/** -* the hijacker for http pattern match. -*/ -class ISrsHttpMatchHijacker -{ -public: - ISrsHttpMatchHijacker(); - virtual ~ISrsHttpMatchHijacker(); -public: - /** - * when match the request failed, no handler to process request. - * @param request the http request message to match the handler. - * @param ph the already matched handler, hijack can rewrite it. - */ - virtual int hijack(SrsHttpMessage* request, ISrsHttpHandler** ph) = 0; -}; - -// ServeMux is an HTTP request multiplexer. -// It matches the URL of each incoming request against a list of registered -// patterns and calls the handler for the pattern that -// most closely matches the URL. -// -// Patterns name fixed, rooted paths, like "/favicon.ico", -// or rooted subtrees, like "/images/" (note the trailing slash). -// Longer patterns take precedence over shorter ones, so that -// if there are handlers registered for both "/images/" -// and "/images/thumbnails/", the latter handler will be -// called for paths beginning "/images/thumbnails/" and the -// former will receive requests for any other paths in the -// "/images/" subtree. -// -// Note that since a pattern ending in a slash names a rooted subtree, -// the pattern "/" matches all paths not matched by other registered -// patterns, not just the URL with Path == "/". -// -// Patterns may optionally begin with a host name, restricting matches to -// URLs on that host only. Host-specific patterns take precedence over -// general patterns, so that a handler might register for the two patterns -// "/codesearch" and "codesearch.google.com/" without also taking over -// requests for "http://www.google.com/". -// -// ServeMux also takes care of sanitizing the URL request path, -// redirecting any request containing . or .. elements to an -// equivalent .- and ..-free URL. -class SrsHttpServeMux -{ -private: - // the pattern handler, to handle the http request. - std::map entries; - // the vhost handler. - // when find the handler to process the request, - // append the matched vhost when pattern not starts with /, - // for example, for pattern /live/livestream.flv of vhost ossrs.net, - // the path will rewrite to ossrs.net/live/livestream.flv - std::map vhosts; - // all hijackers for http match. - // for example, the hstrs(http stream trigger rtmp source) - // can hijack and install handler when request incoming and no handler. - std::vector hijackers; -public: - SrsHttpServeMux(); - virtual ~SrsHttpServeMux(); -public: - /** - * initialize the http serve mux. - */ - virtual int initialize(); - /** - * hijack the http match. - */ - virtual void hijack(ISrsHttpMatchHijacker* h); - virtual void unhijack(ISrsHttpMatchHijacker* h); -public: - // Handle registers the handler for the given pattern. - // If a handler already exists for pattern, Handle panics. - virtual int handle(std::string pattern, ISrsHttpHandler* handler); -// interface ISrsHttpHandler -public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); -private: - virtual int find_handler(SrsHttpMessage* r, ISrsHttpHandler** ph); - virtual int match(SrsHttpMessage* r, ISrsHttpHandler** ph); - virtual bool path_match(std::string pattern, std::string path); -}; - -/** -* response writer use st socket -*/ + * response writer use st socket + */ class SrsHttpResponseWriter : public ISrsHttpResponseWriter { private: @@ -402,7 +58,7 @@ private: private: // reply header has been (logically) written bool header_wrote; - // status code passed to WriteHeader + // status code passed to WriteHeader int status; private: // explicitly-declared Content-Length; or -1 @@ -427,8 +83,8 @@ public: }; /** -* response reader use st socket. -*/ + * response reader use st socket. + */ class SrsHttpResponseReader : virtual public ISrsHttpResponseReader { private: @@ -447,10 +103,10 @@ public: virtual ~SrsHttpResponseReader(); public: /** - * initialize the response reader with buffer. - */ + * initialize the response reader with buffer. + */ virtual int initialize(SrsFastBuffer* buffer); -// interface ISrsHttpResponseReader + // interface ISrsHttpResponseReader public: virtual bool eof(); virtual int read(char* data, int nb_data, int* nb_read); @@ -469,31 +125,31 @@ typedef std::pair SrsHttpHeaderField; // usage. In addition to the notes on the fields below, see the // documentation for Request.Write and RoundTripper. /** -* the http message, request or response. -*/ -class SrsHttpMessage + * the http message, request or response. + */ +class SrsHttpMessage : public ISrsHttpMessage { private: /** - * parsed url. - */ + * parsed url. + */ std::string _url; /** - * the extension of file, for example, .flv - */ + * the extension of file, for example, .flv + */ std::string _ext; /** - * parsed http header. - */ + * parsed http header. + */ http_parser _header; /** - * body object, reader object. - * @remark, user can get body in string by get_body(). - */ + * body object, reader object. + * @remark, user can get body in string by get_body(). + */ SrsHttpResponseReader* _body; /** - * whether the body is chunked. - */ + * whether the body is chunked. + */ bool chunked; /** * whether the request indicates should keep alive @@ -501,12 +157,12 @@ private: */ bool keep_alive; /** - * uri parser - */ + * uri parser + */ SrsHttpUri* _uri; /** - * use a buffer to read and send ts file. - */ + * use a buffer to read and send ts file. + */ // TODO: FIXME: remove it. char* _http_ts_send_buffer; // http headers @@ -520,20 +176,19 @@ public: virtual ~SrsHttpMessage(); public: /** - * set the original messages, then update the message. - */ - virtual int update(std::string url, http_parser* header, + * set the original messages, then update the message. + */ + virtual int update(std::string url, http_parser* header, SrsFastBuffer* body, std::vector& headers ); -public: - virtual char* http_ts_send_buffer(); +private: virtual SrsConnection* connection(); public: virtual u_int8_t method(); virtual u_int16_t status_code(); /** - * method helpers. - */ + * method helpers. + */ virtual std::string method_str(); virtual bool is_http_get(); virtual bool is_http_put(); @@ -541,57 +196,57 @@ public: virtual bool is_http_delete(); virtual bool is_http_options(); /** - * whether body is chunked encoding, for reader only. - */ + * whether body is chunked encoding, for reader only. + */ virtual bool is_chunked(); /** * whether should keep the connection alive. */ virtual bool is_keep_alive(); /** - * the uri contains the host and path. - */ + * the uri contains the host and path. + */ virtual std::string uri(); /** - * the url maybe the path. - */ + * the url maybe the path. + */ virtual std::string url(); virtual std::string host(); virtual std::string path(); virtual std::string ext(); public: /** - * read body to string. - * @remark for small http body. - */ + * read body to string. + * @remark for small http body. + */ virtual int body_read_all(std::string& body); /** - * get the body reader, to read one by one. - * @remark when body is very large, or chunked, use this. - */ + * get the body reader, to read one by one. + * @remark when body is very large, or chunked, use this. + */ virtual ISrsHttpResponseReader* body_reader(); /** - * the content length, -1 for chunked or not set. - */ + * the content length, -1 for chunked or not set. + */ virtual int64_t content_length(); /** - * get the param in query string, - * for instance, query is "start=100&end=200", - * then query_get("start") is "100", and query_get("end") is "200" - */ + * get the param in query string, + * for instance, query is "start=100&end=200", + * then query_get("start") is "100", and query_get("end") is "200" + */ virtual std::string query_get(std::string key); /** - * get the headers. - */ + * get the headers. + */ virtual int request_header_count(); virtual std::string request_header_key_at(int index); virtual std::string request_header_value_at(int index); virtual std::string get_request_header(std::string name); public: /** - * convert the http message to a request. - * @remark user must free the return request. - */ + * convert the http message to a request. + * @remark user must free the return request. + */ virtual SrsRequest* to_request(std::string vhost); }; @@ -631,7 +286,7 @@ public: * or error and *ppmsg must be NULL. * @remark, if success, *ppmsg always NOT-NULL, *ppmsg always is_complete(). */ - virtual int parse_message(SrsStSocket* skt, SrsConnection* conn, SrsHttpMessage** ppmsg); + virtual int parse_message(SrsStSocket* skt, SrsConnection* conn, ISrsHttpMessage** ppmsg); private: /** * parse the HTTP message to member field: msg. diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index cf9a3cc34..d29fca0b5 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -48,7 +48,7 @@ SrsGoApiRoot::~SrsGoApiRoot() { } -int SrsGoApiRoot::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiRoot::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; @@ -70,7 +70,7 @@ SrsGoApiApi::~SrsGoApiApi() { } -int SrsGoApiApi::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiApi::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; @@ -92,7 +92,7 @@ SrsGoApiV1::~SrsGoApiV1() { } -int SrsGoApiV1::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiV1::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; @@ -123,7 +123,7 @@ SrsGoApiVersion::~SrsGoApiVersion() { } -int SrsGoApiVersion::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiVersion::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; @@ -148,7 +148,7 @@ SrsGoApiSummaries::~SrsGoApiSummaries() { } -int SrsGoApiSummaries::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiSummaries::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; srs_api_dump_summaries(ss); @@ -163,7 +163,7 @@ SrsGoApiRusages::~SrsGoApiRusages() { } -int SrsGoApiRusages::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* req) +int SrsGoApiRusages::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* req) { std::stringstream ss; @@ -204,7 +204,7 @@ SrsGoApiSelfProcStats::~SrsGoApiSelfProcStats() { } -int SrsGoApiSelfProcStats::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiSelfProcStats::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; @@ -274,7 +274,7 @@ SrsGoApiSystemProcStats::~SrsGoApiSystemProcStats() { } -int SrsGoApiSystemProcStats::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiSystemProcStats::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; @@ -309,7 +309,7 @@ SrsGoApiMemInfos::~SrsGoApiMemInfos() { } -int SrsGoApiMemInfos::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiMemInfos::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; @@ -345,7 +345,7 @@ SrsGoApiAuthors::~SrsGoApiAuthors() { } -int SrsGoApiAuthors::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiAuthors::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; @@ -370,9 +370,9 @@ SrsGoApiRequests::~SrsGoApiRequests() { } -int SrsGoApiRequests::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiRequests::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { - SrsHttpMessage* req = r; + ISrsHttpMessage* req = r; std::stringstream ss; @@ -431,7 +431,7 @@ SrsGoApiVhosts::~SrsGoApiVhosts() { } -int SrsGoApiVhosts::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiVhosts::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream data; SrsStatistic* stat = SrsStatistic::instance(); @@ -456,7 +456,7 @@ SrsGoApiStreams::~SrsGoApiStreams() { } -int SrsGoApiStreams::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiStreams::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream data; SrsStatistic* stat = SrsStatistic::instance(); @@ -529,7 +529,7 @@ int SrsHttpApi::do_cycle() // process http messages. for (;;) { - SrsHttpMessage* req = NULL; + ISrsHttpMessage* req = NULL; // get a http message if ((ret = parser->parse_message(&skt, this, &req)) != ERROR_SUCCESS) { @@ -540,7 +540,7 @@ int SrsHttpApi::do_cycle() srs_assert(req); // always free it in this scope. - SrsAutoFree(SrsHttpMessage, req); + SrsAutoFree(ISrsHttpMessage, req); // TODO: FIXME: use the post body. std::string res; @@ -566,7 +566,7 @@ int SrsHttpApi::do_cycle() return ret; } -int SrsHttpApi::process_request(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsHttpApi::process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; diff --git a/trunk/src/app/srs_app_http_api.hpp b/trunk/src/app/srs_app_http_api.hpp index 66e971088..1f4f37419 100644 --- a/trunk/src/app/srs_app_http_api.hpp +++ b/trunk/src/app/srs_app_http_api.hpp @@ -33,7 +33,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifdef SRS_AUTO_HTTP_API class SrsStSocket; -class SrsHttpMessage; +class ISrsHttpMessage; class SrsHttpParser; class SrsHttpHandler; @@ -48,7 +48,7 @@ public: SrsGoApiRoot(); virtual ~SrsGoApiRoot(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiApi : public ISrsHttpHandler @@ -57,7 +57,7 @@ public: SrsGoApiApi(); virtual ~SrsGoApiApi(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiV1 : public ISrsHttpHandler @@ -66,7 +66,7 @@ public: SrsGoApiV1(); virtual ~SrsGoApiV1(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiVersion : public ISrsHttpHandler @@ -75,7 +75,7 @@ public: SrsGoApiVersion(); virtual ~SrsGoApiVersion(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiSummaries : public ISrsHttpHandler @@ -84,7 +84,7 @@ public: SrsGoApiSummaries(); virtual ~SrsGoApiSummaries(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiRusages : public ISrsHttpHandler @@ -93,7 +93,7 @@ public: SrsGoApiRusages(); virtual ~SrsGoApiRusages(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiSelfProcStats : public ISrsHttpHandler @@ -102,7 +102,7 @@ public: SrsGoApiSelfProcStats(); virtual ~SrsGoApiSelfProcStats(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiSystemProcStats : public ISrsHttpHandler @@ -111,7 +111,7 @@ public: SrsGoApiSystemProcStats(); virtual ~SrsGoApiSystemProcStats(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiMemInfos : public ISrsHttpHandler @@ -120,7 +120,7 @@ public: SrsGoApiMemInfos(); virtual ~SrsGoApiMemInfos(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiAuthors : public ISrsHttpHandler @@ -129,7 +129,7 @@ public: SrsGoApiAuthors(); virtual ~SrsGoApiAuthors(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiRequests : public ISrsHttpHandler @@ -138,7 +138,7 @@ public: SrsGoApiRequests(); virtual ~SrsGoApiRequests(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiVhosts : public ISrsHttpHandler @@ -147,7 +147,7 @@ public: SrsGoApiVhosts(); virtual ~SrsGoApiVhosts(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiStreams : public ISrsHttpHandler @@ -156,7 +156,7 @@ public: SrsGoApiStreams(); virtual ~SrsGoApiStreams(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsHttpApi : public SrsConnection @@ -177,7 +177,7 @@ public: protected: virtual int do_cycle(); private: - virtual int process_request(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; #endif diff --git a/trunk/src/app/srs_app_http_client.cpp b/trunk/src/app/srs_app_http_client.cpp index 377f9a79b..e769e6eb3 100644 --- a/trunk/src/app/srs_app_http_client.cpp +++ b/trunk/src/app/srs_app_http_client.cpp @@ -71,7 +71,7 @@ int SrsHttpClient::initialize(string h, int p, int64_t t_us) return ret; } -int SrsHttpClient::post(string path, string req, SrsHttpMessage** ppmsg) +int SrsHttpClient::post(string path, string req, ISrsHttpMessage** ppmsg) { *ppmsg = NULL; @@ -104,7 +104,7 @@ int SrsHttpClient::post(string path, string req, SrsHttpMessage** ppmsg) return ret; } - SrsHttpMessage* msg = NULL; + ISrsHttpMessage* msg = NULL; if ((ret = parser->parse_message(skt, NULL, &msg)) != ERROR_SUCCESS) { srs_error("parse http post response failed. ret=%d", ret); return ret; @@ -117,7 +117,7 @@ int SrsHttpClient::post(string path, string req, SrsHttpMessage** ppmsg) return ret; } -int SrsHttpClient::get(string path, std::string req, SrsHttpMessage** ppmsg) +int SrsHttpClient::get(string path, std::string req, ISrsHttpMessage** ppmsg) { *ppmsg = NULL; @@ -150,7 +150,7 @@ int SrsHttpClient::get(string path, std::string req, SrsHttpMessage** ppmsg) return ret; } - SrsHttpMessage* msg = NULL; + ISrsHttpMessage* msg = NULL; if ((ret = parser->parse_message(skt, NULL, &msg)) != ERROR_SUCCESS) { srs_error("parse http post response failed. ret=%d", ret); return ret; diff --git a/trunk/src/app/srs_app_http_client.hpp b/trunk/src/app/srs_app_http_client.hpp index 87f2f36bd..8a2dc88b7 100644 --- a/trunk/src/app/srs_app_http_client.hpp +++ b/trunk/src/app/srs_app_http_client.hpp @@ -37,7 +37,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class SrsHttpUri; class SrsHttpParser; -class SrsHttpMessage; +class ISrsHttpMessage; class SrsStSocket; // the default timeout for http client. @@ -73,14 +73,14 @@ public: * @param req the data post to uri. empty string to ignore. * @param ppmsg output the http message to read the response. */ - virtual int post(std::string path, std::string req, SrsHttpMessage** ppmsg); + virtual int post(std::string path, std::string req, ISrsHttpMessage** ppmsg); /** * to get data from the uri. * @param the path to request on. * @param req the data post to uri. empty string to ignore. * @param ppmsg output the http message to read the response. */ - virtual int get(std::string path, std::string req, SrsHttpMessage** ppmsg); + virtual int get(std::string path, std::string req, ISrsHttpMessage** ppmsg); private: virtual void disconnect(); virtual int connect(); diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp index 5c99f79ef..12fda521c 100644 --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -60,7 +60,7 @@ SrsVodStream::~SrsVodStream() { } -int SrsVodStream::serve_flv_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, string fullpath, int offset) +int SrsVodStream::serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int offset) { int ret = ERROR_SUCCESS; @@ -144,7 +144,7 @@ int SrsVodStream::serve_flv_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, return ret; } -int SrsVodStream::serve_mp4_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, string fullpath, int start, int end) +int SrsVodStream::serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int start, int end) { int ret = ERROR_SUCCESS; @@ -567,7 +567,7 @@ SrsLiveStream::~SrsLiveStream() srs_freep(req); } -int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; @@ -743,7 +743,7 @@ void SrsHlsM3u8Stream::set_m3u8(std::string v) m3u8 = v; } -int SrsHlsM3u8Stream::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsHlsM3u8Stream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; @@ -775,7 +775,7 @@ void SrsHlsTsStream::set_ts(std::string v) ts = v; } -int SrsHlsTsStream::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsHlsTsStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; @@ -1110,7 +1110,7 @@ int SrsHttpServer::on_reload_vhost_hls(string vhost) return ret; } -int SrsHttpServer::hijack(SrsHttpMessage* request, ISrsHttpHandler** ph) +int SrsHttpServer::hijack(ISrsHttpMessage* request, ISrsHttpHandler** ph) { int ret = ERROR_SUCCESS; @@ -1169,8 +1169,12 @@ int SrsHttpServer::hijack(SrsHttpMessage* request, ISrsHttpHandler** ph) } } + // convert to concreate class. + SrsHttpMessage* hreq = dynamic_cast(request); + srs_assert(hreq); + // hijack for entry. - SrsRequest* r = request->to_request(vhost->arg0()); + SrsRequest* r = hreq->to_request(vhost->arg0()); SrsAutoFree(SrsRequest, r); SrsSource* s = SrsSource::fetch(r); if (!s) { @@ -1389,7 +1393,7 @@ int SrsHttpConn::do_cycle() // process http messages. for (;;) { - SrsHttpMessage* req = NULL; + ISrsHttpMessage* req = NULL; // get a http message if ((ret = parser->parse_message(&skt, this, &req)) != ERROR_SUCCESS) { @@ -1400,7 +1404,7 @@ int SrsHttpConn::do_cycle() srs_assert(req); // always free it in this scope. - SrsAutoFree(SrsHttpMessage, req); + SrsAutoFree(ISrsHttpMessage, req); // may should discard the body. if ((ret = on_got_http_message(req)) != ERROR_SUCCESS) { @@ -1423,7 +1427,7 @@ int SrsHttpConn::do_cycle() return ret; } -int SrsHttpConn::process_request(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsHttpConn::process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; @@ -1450,7 +1454,7 @@ SrsStaticHttpConn::~SrsStaticHttpConn() { } -int SrsStaticHttpConn::on_got_http_message(SrsHttpMessage* msg) +int SrsStaticHttpConn::on_got_http_message(ISrsHttpMessage* msg) { int ret = ERROR_SUCCESS; diff --git a/trunk/src/app/srs_app_http_conn.hpp b/trunk/src/app/srs_app_http_conn.hpp index 5b4bf62d5..e7a971e9f 100644 --- a/trunk/src/app/srs_app_http_conn.hpp +++ b/trunk/src/app/srs_app_http_conn.hpp @@ -49,7 +49,7 @@ class SrsAacEncoder; class SrsMp3Encoder; class SrsFlvEncoder; class SrsHttpParser; -class SrsHttpMessage; +class ISrsHttpMessage; class SrsHttpHandler; class SrsMessageQueue; class SrsSharedPtrMessage; @@ -66,8 +66,8 @@ public: SrsVodStream(std::string root_dir); virtual ~SrsVodStream(); protected: - virtual int serve_flv_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int offset); - virtual int serve_mp4_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int start, int end); + virtual int serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int offset); + virtual int serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int start, int end); }; /** @@ -243,7 +243,7 @@ public: SrsLiveStream(SrsSource* s, SrsRequest* r, SrsStreamCache* c); virtual ~SrsLiveStream(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); private: virtual int streaming_send_messages(ISrsStreamEncoder* enc, SrsSharedPtrMessage** msgs, int nb_msgs); }; @@ -289,7 +289,7 @@ public: public: virtual void set_m3u8(std::string v); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; /** @@ -305,7 +305,7 @@ public: public: virtual void set_ts(std::string v); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; /** @@ -365,7 +365,7 @@ public: virtual int on_reload_vhost_hls(std::string vhost); // interface ISrsHttpMatchHijacker public: - virtual int hijack(SrsHttpMessage* request, ISrsHttpHandler** ph); + virtual int hijack(ISrsHttpMessage* request, ISrsHttpHandler** ph); private: virtual int initialize_static_file(); virtual int initialize_flv_streaming(); @@ -392,9 +392,9 @@ protected: // when got http message, // for the static service or api, discard any body. // for the stream caster, for instance, http flv streaming, may discard the flv header or not. - virtual int on_got_http_message(SrsHttpMessage* msg) = 0; + virtual int on_got_http_message(ISrsHttpMessage* msg) = 0; private: - virtual int process_request(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsStaticHttpConn : public SrsHttpConn @@ -403,7 +403,7 @@ public: SrsStaticHttpConn(IConnectionManager* cm, st_netfd_t fd, SrsHttpServeMux* m); virtual ~SrsStaticHttpConn(); public: - virtual int on_got_http_message(SrsHttpMessage* msg); + virtual int on_got_http_message(ISrsHttpMessage* msg); }; #endif diff --git a/trunk/src/app/srs_app_http_hooks.cpp b/trunk/src/app/srs_app_http_hooks.cpp index 71b1e6348..1406df42e 100644 --- a/trunk/src/app/srs_app_http_hooks.cpp +++ b/trunk/src/app/srs_app_http_hooks.cpp @@ -371,11 +371,11 @@ int SrsHttpHooks::on_hls_notify(std::string url, SrsRequest* req, std::string ts } srs_warn("GET %s", path.c_str()); - SrsHttpMessage* msg = NULL; + ISrsHttpMessage* msg = NULL; if ((ret = http.get(path.c_str(), "", &msg)) != ERROR_SUCCESS) { return ret; } - SrsAutoFree(SrsHttpMessage, msg); + SrsAutoFree(ISrsHttpMessage, msg); int nb_buf = srs_min(nb_notify, SRS_HTTP_READ_BUFFER); char* buf = new char[nb_buf]; @@ -416,11 +416,11 @@ int SrsHttpHooks::do_post(std::string url, std::string req, int& code, string& r return ret; } - SrsHttpMessage* msg = NULL; + ISrsHttpMessage* msg = NULL; if ((ret = http.post(uri.get_path(), req, &msg)) != ERROR_SUCCESS) { return ret; } - SrsAutoFree(SrsHttpMessage, msg); + SrsAutoFree(ISrsHttpMessage, msg); code = msg->status_code(); if ((ret = msg->body_read_all(res)) != ERROR_SUCCESS) { diff --git a/trunk/src/main/srs_main_ingest_hls.cpp b/trunk/src/main/srs_main_ingest_hls.cpp index 4de2740ca..ea17b60b5 100644 --- a/trunk/src/main/srs_main_ingest_hls.cpp +++ b/trunk/src/main/srs_main_ingest_hls.cpp @@ -383,14 +383,14 @@ int SrsIngestSrsInput::parseM3u8(SrsHttpUri* url, double& td, double& duration) return ret; } - SrsHttpMessage* msg = NULL; + ISrsHttpMessage* msg = NULL; if ((ret = client.get(url->get_path(), "", &msg)) != ERROR_SUCCESS) { srs_error("HTTP GET %s failed. ret=%d", url->get_url(), ret); return ret; } srs_assert(msg); - SrsAutoFree(SrsHttpMessage, msg); + SrsAutoFree(ISrsHttpMessage, msg); std::string body; if ((ret = msg->body_read_all(body)) != ERROR_SUCCESS) { @@ -605,14 +605,14 @@ int SrsIngestSrsInput::SrsTsPiece::fetch(string m3u8) return ret; } - SrsHttpMessage* msg = NULL; + ISrsHttpMessage* msg = NULL; if ((ret = client.get(uri.get_path(), "", &msg)) != ERROR_SUCCESS) { srs_error("HTTP GET %s failed. ret=%d", uri.get_url(), ret); return ret; } srs_assert(msg); - SrsAutoFree(SrsHttpMessage, msg); + SrsAutoFree(ISrsHttpMessage, msg); if ((ret = msg->body_read_all(body)) != ERROR_SUCCESS) { srs_error("read ts failed. ret=%d", ret); diff --git a/trunk/src/protocol/srs_http_stack.cpp b/trunk/src/protocol/srs_http_stack.cpp index 2cbdb333f..6e46e23e2 100644 --- a/trunk/src/protocol/srs_http_stack.cpp +++ b/trunk/src/protocol/srs_http_stack.cpp @@ -22,3 +22,717 @@ */ #include + +#include +using namespace std; + +#include +#include +#include +#include + +#define SRS_HTTP_DEFAULT_PAGE "index.html" + +// get the status text of code. +string srs_generate_http_status_text(int status) +{ + static std::map _status_map; + if (_status_map.empty()) { + _status_map[SRS_CONSTS_HTTP_Continue ] = SRS_CONSTS_HTTP_Continue_str ; + _status_map[SRS_CONSTS_HTTP_SwitchingProtocols ] = SRS_CONSTS_HTTP_SwitchingProtocols_str ; + _status_map[SRS_CONSTS_HTTP_OK ] = SRS_CONSTS_HTTP_OK_str ; + _status_map[SRS_CONSTS_HTTP_Created ] = SRS_CONSTS_HTTP_Created_str ; + _status_map[SRS_CONSTS_HTTP_Accepted ] = SRS_CONSTS_HTTP_Accepted_str ; + _status_map[SRS_CONSTS_HTTP_NonAuthoritativeInformation ] = SRS_CONSTS_HTTP_NonAuthoritativeInformation_str ; + _status_map[SRS_CONSTS_HTTP_NoContent ] = SRS_CONSTS_HTTP_NoContent_str ; + _status_map[SRS_CONSTS_HTTP_ResetContent ] = SRS_CONSTS_HTTP_ResetContent_str ; + _status_map[SRS_CONSTS_HTTP_PartialContent ] = SRS_CONSTS_HTTP_PartialContent_str ; + _status_map[SRS_CONSTS_HTTP_MultipleChoices ] = SRS_CONSTS_HTTP_MultipleChoices_str ; + _status_map[SRS_CONSTS_HTTP_MovedPermanently ] = SRS_CONSTS_HTTP_MovedPermanently_str ; + _status_map[SRS_CONSTS_HTTP_Found ] = SRS_CONSTS_HTTP_Found_str ; + _status_map[SRS_CONSTS_HTTP_SeeOther ] = SRS_CONSTS_HTTP_SeeOther_str ; + _status_map[SRS_CONSTS_HTTP_NotModified ] = SRS_CONSTS_HTTP_NotModified_str ; + _status_map[SRS_CONSTS_HTTP_UseProxy ] = SRS_CONSTS_HTTP_UseProxy_str ; + _status_map[SRS_CONSTS_HTTP_TemporaryRedirect ] = SRS_CONSTS_HTTP_TemporaryRedirect_str ; + _status_map[SRS_CONSTS_HTTP_BadRequest ] = SRS_CONSTS_HTTP_BadRequest_str ; + _status_map[SRS_CONSTS_HTTP_Unauthorized ] = SRS_CONSTS_HTTP_Unauthorized_str ; + _status_map[SRS_CONSTS_HTTP_PaymentRequired ] = SRS_CONSTS_HTTP_PaymentRequired_str ; + _status_map[SRS_CONSTS_HTTP_Forbidden ] = SRS_CONSTS_HTTP_Forbidden_str ; + _status_map[SRS_CONSTS_HTTP_NotFound ] = SRS_CONSTS_HTTP_NotFound_str ; + _status_map[SRS_CONSTS_HTTP_MethodNotAllowed ] = SRS_CONSTS_HTTP_MethodNotAllowed_str ; + _status_map[SRS_CONSTS_HTTP_NotAcceptable ] = SRS_CONSTS_HTTP_NotAcceptable_str ; + _status_map[SRS_CONSTS_HTTP_ProxyAuthenticationRequired ] = SRS_CONSTS_HTTP_ProxyAuthenticationRequired_str ; + _status_map[SRS_CONSTS_HTTP_RequestTimeout ] = SRS_CONSTS_HTTP_RequestTimeout_str ; + _status_map[SRS_CONSTS_HTTP_Conflict ] = SRS_CONSTS_HTTP_Conflict_str ; + _status_map[SRS_CONSTS_HTTP_Gone ] = SRS_CONSTS_HTTP_Gone_str ; + _status_map[SRS_CONSTS_HTTP_LengthRequired ] = SRS_CONSTS_HTTP_LengthRequired_str ; + _status_map[SRS_CONSTS_HTTP_PreconditionFailed ] = SRS_CONSTS_HTTP_PreconditionFailed_str ; + _status_map[SRS_CONSTS_HTTP_RequestEntityTooLarge ] = SRS_CONSTS_HTTP_RequestEntityTooLarge_str ; + _status_map[SRS_CONSTS_HTTP_RequestURITooLarge ] = SRS_CONSTS_HTTP_RequestURITooLarge_str ; + _status_map[SRS_CONSTS_HTTP_UnsupportedMediaType ] = SRS_CONSTS_HTTP_UnsupportedMediaType_str ; + _status_map[SRS_CONSTS_HTTP_RequestedRangeNotSatisfiable ] = SRS_CONSTS_HTTP_RequestedRangeNotSatisfiable_str ; + _status_map[SRS_CONSTS_HTTP_ExpectationFailed ] = SRS_CONSTS_HTTP_ExpectationFailed_str ; + _status_map[SRS_CONSTS_HTTP_InternalServerError ] = SRS_CONSTS_HTTP_InternalServerError_str ; + _status_map[SRS_CONSTS_HTTP_NotImplemented ] = SRS_CONSTS_HTTP_NotImplemented_str ; + _status_map[SRS_CONSTS_HTTP_BadGateway ] = SRS_CONSTS_HTTP_BadGateway_str ; + _status_map[SRS_CONSTS_HTTP_ServiceUnavailable ] = SRS_CONSTS_HTTP_ServiceUnavailable_str ; + _status_map[SRS_CONSTS_HTTP_GatewayTimeout ] = SRS_CONSTS_HTTP_GatewayTimeout_str ; + _status_map[SRS_CONSTS_HTTP_HTTPVersionNotSupported ] = SRS_CONSTS_HTTP_HTTPVersionNotSupported_str ; + } + + std::string status_text; + if (_status_map.find(status) == _status_map.end()) { + status_text = "Status Unknown"; + } else { + status_text = _status_map[status]; + } + + return status_text; +} + +// bodyAllowedForStatus reports whether a given response status code +// permits a body. See RFC2616, section 4.4. +bool srs_go_http_body_allowd(int status) +{ + if (status >= 100 && status <= 199) { + return false; + } else if (status == 204 || status == 304) { + return false; + } + + return true; +} + +// DetectContentType implements the algorithm described +// at http://mimesniff.spec.whatwg.org/ to determine the +// Content-Type of the given data. It considers at most the +// first 512 bytes of data. DetectContentType always returns +// a valid MIME type: if it cannot determine a more specific one, it +// returns "application/octet-stream". +string srs_go_http_detect(char* data, int size) +{ + // detect only when data specified. + if (data) { + } + return "application/octet-stream"; // fallback +} + +// Error replies to the request with the specified error message and HTTP code. +// The error message should be plain text. +int srs_go_http_error(ISrsHttpResponseWriter* w, int code, string error) +{ + int ret = ERROR_SUCCESS; + + w->header()->set_content_type("text/plain; charset=utf-8"); + w->header()->set_content_length(error.length()); + w->write_header(code); + w->write((char*)error.data(), (int)error.length()); + + return ret; +} + +int srs_http_response_json(ISrsHttpResponseWriter* w, string data) +{ + SrsHttpHeader* h = w->header(); + + h->set_content_length(data.length()); + h->set_content_type("application/json"); + + return w->write((char*)data.data(), (int)data.length()); +} + +SrsHttpHeader::SrsHttpHeader() +{ +} + +SrsHttpHeader::~SrsHttpHeader() +{ +} + +void SrsHttpHeader::set(string key, string value) +{ + headers[key] = value; +} + +string SrsHttpHeader::get(string key) +{ + std::string v; + + if (headers.find(key) != headers.end()) { + v = headers[key]; + } + + return v; +} + +int64_t SrsHttpHeader::content_length() +{ + std::string cl = get("Content-Length"); + + if (cl.empty()) { + return -1; + } + + return (int64_t)::atof(cl.c_str()); +} + +void SrsHttpHeader::set_content_length(int64_t size) +{ + char buf[64]; + snprintf(buf, sizeof(buf), "%"PRId64, size); + set("Content-Length", buf); +} + +string SrsHttpHeader::content_type() +{ + return get("Content-Type"); +} + +void SrsHttpHeader::set_content_type(string ct) +{ + set("Content-Type", ct); +} + +void SrsHttpHeader::write(stringstream& ss) +{ + std::map::iterator it; + for (it = headers.begin(); it != headers.end(); ++it) { + ss << it->first << ": " << it->second << SRS_HTTP_CRLF; + } +} + +ISrsHttpResponseWriter::ISrsHttpResponseWriter() +{ +} + +ISrsHttpResponseWriter::~ISrsHttpResponseWriter() +{ +} + +ISrsHttpResponseReader::ISrsHttpResponseReader() +{ +} + +ISrsHttpResponseReader::~ISrsHttpResponseReader() +{ +} + +ISrsHttpHandler::ISrsHttpHandler() +{ + entry = NULL; +} + +ISrsHttpHandler::~ISrsHttpHandler() +{ +} + +SrsHttpRedirectHandler::SrsHttpRedirectHandler(string u, int c) +{ + url = u; + code = c; +} + +SrsHttpRedirectHandler::~SrsHttpRedirectHandler() +{ +} + +int SrsHttpRedirectHandler::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) +{ + int ret = ERROR_SUCCESS; + // TODO: FIXME: implements it. + return ret; +} + +SrsHttpNotFoundHandler::SrsHttpNotFoundHandler() +{ +} + +SrsHttpNotFoundHandler::~SrsHttpNotFoundHandler() +{ +} + +int SrsHttpNotFoundHandler::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) +{ + return srs_go_http_error(w, SRS_CONSTS_HTTP_NotFound, SRS_CONSTS_HTTP_NotFound_str); +} + +SrsHttpFileServer::SrsHttpFileServer(string root_dir) +{ + dir = root_dir; +} + +SrsHttpFileServer::~SrsHttpFileServer() +{ +} + +int SrsHttpFileServer::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) +{ + string upath = r->path(); + + // add default pages. + if (srs_string_ends_with(upath, "/")) { + upath += SRS_HTTP_DEFAULT_PAGE; + } + + string fullpath = dir + "/"; + + // remove the virtual directory. + srs_assert(entry); + size_t pos = entry->pattern.find("/"); + if (upath.length() > entry->pattern.length() && pos != string::npos) { + fullpath += upath.substr(entry->pattern.length() - pos); + } else { + fullpath += upath; + } + + // stat current dir, if exists, return error. + if (!srs_path_exists(fullpath)) { + srs_warn("http miss file=%s, pattern=%s, upath=%s", + fullpath.c_str(), entry->pattern.c_str(), upath.c_str()); + return SrsHttpNotFoundHandler().serve_http(w, r); + } + srs_trace("http match file=%s, pattern=%s, upath=%s", + fullpath.c_str(), entry->pattern.c_str(), upath.c_str()); + + // handle file according to its extension. + // use vod stream for .flv/.fhv + if (srs_string_ends_with(fullpath, ".flv") || srs_string_ends_with(fullpath, ".fhv")) { + return serve_flv_file(w, r, fullpath); + } else if (srs_string_ends_with(fullpath, ".mp4")) { + return serve_mp4_file(w, r, fullpath); + } + + // serve common static file. + return serve_file(w, r, fullpath); +} + +int SrsHttpFileServer::serve_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath) +{ + int ret = ERROR_SUCCESS; + + // open the target file. + SrsFileReader fs; + + if ((ret = fs.open(fullpath)) != ERROR_SUCCESS) { + srs_warn("open file %s failed, ret=%d", fullpath.c_str(), ret); + return ret; + } + + int64_t length = fs.filesize(); + + // unset the content length to encode in chunked encoding. + w->header()->set_content_length(length); + + static std::map _mime; + if (_mime.empty()) { + _mime[".ts"] = "video/MP2T"; + _mime[".flv"] = "video/x-flv"; + _mime[".m4v"] = "video/x-m4v"; + _mime[".3gpp"] = "video/3gpp"; + _mime[".3gp"] = "video/3gpp"; + _mime[".mp4"] = "video/mp4"; + _mime[".aac"] = "audio/x-aac"; + _mime[".mp3"] = "audio/mpeg"; + _mime[".m4a"] = "audio/x-m4a"; + _mime[".ogg"] = "audio/ogg"; + // @see hls-m3u8-draft-pantos-http-live-streaming-12.pdf, page 5. + _mime[".m3u8"] = "application/vnd.apple.mpegurl"; // application/x-mpegURL + _mime[".rss"] = "application/rss+xml"; + _mime[".json"] = "application/json"; + _mime[".swf"] = "application/x-shockwave-flash"; + _mime[".doc"] = "application/msword"; + _mime[".zip"] = "application/zip"; + _mime[".rar"] = "application/x-rar-compressed"; + _mime[".xml"] = "text/xml"; + _mime[".html"] = "text/html"; + _mime[".js"] = "text/javascript"; + _mime[".css"] = "text/css"; + _mime[".ico"] = "image/x-icon"; + _mime[".png"] = "image/png"; + _mime[".jpeg"] = "image/jpeg"; + _mime[".jpg"] = "image/jpeg"; + _mime[".gif"] = "image/gif"; + } + + if (true) { + size_t pos; + std::string ext = fullpath; + if ((pos = ext.rfind(".")) != string::npos) { + ext = ext.substr(pos); + } + + if (_mime.find(ext) == _mime.end()) { + w->header()->set_content_type("application/octet-stream"); + } else { + w->header()->set_content_type(_mime[ext]); + } + } + + // write body. + int64_t left = length; + if ((ret = copy(w, &fs, r, (int)left)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("read file=%s size=%d failed, ret=%d", fullpath.c_str(), left, ret); + } + return ret; + } + + return w->final_request(); +} + +int SrsHttpFileServer::serve_flv_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath) +{ + std::string start = r->query_get("start"); + if (start.empty()) { + return serve_file(w, r, fullpath); + } + + int offset = ::atoi(start.c_str()); + if (offset <= 0) { + return serve_file(w, r, fullpath); + } + + return serve_flv_stream(w, r, fullpath, offset); +} + +int SrsHttpFileServer::serve_mp4_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath) +{ + // for flash to request mp4 range in query string. + // for example, http://digitalprimates.net/dash/DashTest.html?url=http://dashdemo.edgesuite.net/digitalprimates/nexus/oops-20120802-manifest.mpd + std::string range = r->query_get("range"); + // or, use bytes to request range, + // for example, http://dashas.castlabs.com/demo/try.html + if (range.empty()) { + range = r->query_get("bytes"); + } + + // rollback to serve whole file. + size_t pos = string::npos; + if (range.empty() || (pos = range.find("-")) == string::npos) { + return serve_file(w, r, fullpath); + } + + // parse the start in query string + int start = 0; + if (pos > 0) { + start = ::atoi(range.substr(0, pos).c_str()); + } + + // parse end in query string. + int end = -1; + if (pos < range.length() - 1) { + end = ::atoi(range.substr(pos + 1).c_str()); + } + + // invalid param, serve as whole mp4 file. + if (start < 0 || (end != -1 && start > end)) { + return serve_file(w, r, fullpath); + } + + return serve_mp4_stream(w, r, fullpath, start, end); +} + +int SrsHttpFileServer::serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int offset) +{ + return serve_file(w, r, fullpath); +} + +int SrsHttpFileServer::serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int start, int end) +{ + return serve_file(w, r, fullpath); +} + +int SrsHttpFileServer::copy(ISrsHttpResponseWriter* w, SrsFileReader* fs, ISrsHttpMessage* r, int size) +{ + int ret = ERROR_SUCCESS; + + int left = size; + char* buf = r->http_ts_send_buffer(); + + while (left > 0) { + ssize_t nread = -1; + int max_read = srs_min(left, SRS_HTTP_TS_SEND_BUFFER_SIZE); + if ((ret = fs->read(buf, max_read, &nread)) != ERROR_SUCCESS) { + break; + } + + left -= nread; + if ((ret = w->write(buf, (int)nread)) != ERROR_SUCCESS) { + break; + } + } + + return ret; +} + +SrsHttpMuxEntry::SrsHttpMuxEntry() +{ + enabled = true; + explicit_match = false; + handler = NULL; +} + +SrsHttpMuxEntry::~SrsHttpMuxEntry() +{ + srs_freep(handler); +} + +ISrsHttpMatchHijacker::ISrsHttpMatchHijacker() +{ +} + +ISrsHttpMatchHijacker::~ISrsHttpMatchHijacker() +{ +} + +SrsHttpServeMux::SrsHttpServeMux() +{ +} + +SrsHttpServeMux::~SrsHttpServeMux() +{ + std::map::iterator it; + for (it = entries.begin(); it != entries.end(); ++it) { + SrsHttpMuxEntry* entry = it->second; + srs_freep(entry); + } + entries.clear(); + + vhosts.clear(); + hijackers.clear(); +} + +int SrsHttpServeMux::initialize() +{ + int ret = ERROR_SUCCESS; + // TODO: FIXME: implements it. + return ret; +} + +void SrsHttpServeMux::hijack(ISrsHttpMatchHijacker* h) +{ + std::vector::iterator it = ::find(hijackers.begin(), hijackers.end(), h); + if (it != hijackers.end()) { + return; + } + hijackers.push_back(h); +} + +void SrsHttpServeMux::unhijack(ISrsHttpMatchHijacker* h) +{ + std::vector::iterator it = ::find(hijackers.begin(), hijackers.end(), h); + if (it == hijackers.end()) { + return; + } + hijackers.erase(it); +} + +int SrsHttpServeMux::handle(std::string pattern, ISrsHttpHandler* handler) +{ + int ret = ERROR_SUCCESS; + + srs_assert(handler); + + if (pattern.empty()) { + ret = ERROR_HTTP_PATTERN_EMPTY; + srs_error("http: empty pattern. ret=%d", ret); + return ret; + } + + if (entries.find(pattern) != entries.end()) { + SrsHttpMuxEntry* exists = entries[pattern]; + if (exists->explicit_match) { + ret = ERROR_HTTP_PATTERN_DUPLICATED; + srs_error("http: multiple registrations for %s. ret=%d", pattern.c_str(), ret); + return ret; + } + } + + std::string vhost = pattern; + if (pattern.at(0) != '/') { + if (pattern.find("/") != string::npos) { + vhost = pattern.substr(0, pattern.find("/")); + } + vhosts[vhost] = handler; + } + + if (true) { + SrsHttpMuxEntry* entry = new SrsHttpMuxEntry(); + entry->explicit_match = true; + entry->handler = handler; + entry->pattern = pattern; + entry->handler->entry = entry; + + if (entries.find(pattern) != entries.end()) { + SrsHttpMuxEntry* exists = entries[pattern]; + srs_freep(exists); + } + entries[pattern] = entry; + } + + // Helpful behavior: + // If pattern is /tree/, insert an implicit permanent redirect for /tree. + // It can be overridden by an explicit registration. + if (pattern != "/" && !pattern.empty() && pattern.at(pattern.length() - 1) == '/') { + std::string rpattern = pattern.substr(0, pattern.length() - 1); + SrsHttpMuxEntry* entry = NULL; + + // free the exists not explicit entry + if (entries.find(rpattern) != entries.end()) { + SrsHttpMuxEntry* exists = entries[rpattern]; + if (!exists->explicit_match) { + entry = exists; + } + } + + // create implicit redirect. + if (!entry || entry->explicit_match) { + srs_freep(entry); + + entry = new SrsHttpMuxEntry(); + entry->explicit_match = false; + entry->handler = new SrsHttpRedirectHandler(pattern, SRS_CONSTS_HTTP_MovedPermanently); + entry->pattern = pattern; + entry->handler->entry = entry; + + entries[rpattern] = entry; + } + } + + return ret; +} + +int SrsHttpServeMux::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) +{ + int ret = ERROR_SUCCESS; + + ISrsHttpHandler* h = NULL; + if ((ret = find_handler(r, &h)) != ERROR_SUCCESS) { + srs_error("find handler failed. ret=%d", ret); + return ret; + } + + srs_assert(h); + if ((ret = h->serve_http(w, r)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("handler serve http failed. ret=%d", ret); + } + return ret; + } + + return ret; +} + +int SrsHttpServeMux::find_handler(ISrsHttpMessage* r, ISrsHttpHandler** ph) +{ + int ret = ERROR_SUCCESS; + + // TODO: FIXME: support the path . and .. + if (r->url().find("..") != std::string::npos) { + ret = ERROR_HTTP_URL_NOT_CLEAN; + srs_error("htt url not canonical, url=%s. ret=%d", r->url().c_str(), ret); + return ret; + } + + if ((ret = match(r, ph)) != ERROR_SUCCESS) { + srs_error("http match handler failed. ret=%d", ret); + return ret; + } + + // always hijack. + if (!hijackers.empty()) { + // notice all hijacker the match failed. + std::vector::iterator it; + for (it = hijackers.begin(); it != hijackers.end(); ++it) { + ISrsHttpMatchHijacker* hijacker = *it; + if ((ret = hijacker->hijack(r, ph)) != ERROR_SUCCESS) { + srs_error("hijacker match failed. ret=%d", ret); + return ret; + } + } + } + + if (*ph == NULL) { + // TODO: FIXME: memory leak. + *ph = new SrsHttpNotFoundHandler(); + } + + return ret; +} + +int SrsHttpServeMux::match(ISrsHttpMessage* r, ISrsHttpHandler** ph) +{ + int ret = ERROR_SUCCESS; + + std::string path = r->path(); + + // Host-specific pattern takes precedence over generic ones + if (!vhosts.empty() && vhosts.find(r->host()) != vhosts.end()) { + path = r->host() + path; + } + + int nb_matched = 0; + ISrsHttpHandler* h = NULL; + + std::map::iterator it; + for (it = entries.begin(); it != entries.end(); ++it) { + std::string pattern = it->first; + SrsHttpMuxEntry* entry = it->second; + + if (!entry->enabled) { + continue; + } + + if (!path_match(pattern, path)) { + continue; + } + + if (!h || (int)pattern.length() > nb_matched) { + nb_matched = (int)pattern.length(); + h = entry->handler; + } + } + + *ph = h; + + return ret; +} + +bool SrsHttpServeMux::path_match(string pattern, string path) +{ + if (pattern.empty()) { + return false; + } + + int n = (int)pattern.length(); + + // not endswith '/', exactly match. + if (pattern.at(n - 1) != '/') { + return pattern == path; + } + + // endswith '/', match any, + // for example, '/api/' match '/api/[N]' + if ((int)path.length() >= n) { + if (memcmp(pattern.data(), path.data(), n) == 0) { + return true; + } + } + + return false; +} + +ISrsHttpMessage::ISrsHttpMessage() +{ + _http_ts_send_buffer = new char[SRS_HTTP_TS_SEND_BUFFER_SIZE]; +} + +ISrsHttpMessage::~ISrsHttpMessage() +{ + srs_freep(_http_ts_send_buffer); +} + +char* ISrsHttpMessage::http_ts_send_buffer() +{ + return _http_ts_send_buffer; +} diff --git a/trunk/src/protocol/srs_http_stack.hpp b/trunk/src/protocol/srs_http_stack.hpp index d1e147da1..8b14aad0e 100644 --- a/trunk/src/protocol/srs_http_stack.hpp +++ b/trunk/src/protocol/srs_http_stack.hpp @@ -29,4 +29,466 @@ */ #include +#include +#include +#include + +class SrsFileReader; +class SrsHttpHeader; +class ISrsHttpMessage; +class SrsHttpMuxEntry; +class ISrsHttpResponseWriter; + +// http specification +// CR = +#define SRS_HTTP_CR SRS_CONSTS_CR // 0x0D +// LF = +#define SRS_HTTP_LF SRS_CONSTS_LF // 0x0A +// SP = +#define SRS_HTTP_SP ' ' // 0x20 +// HT = +#define SRS_HTTP_HT '\x09' // 0x09 + +// HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all +// protocol elements except the entity-body (see appendix 19.3 for +// tolerant applications). +#define SRS_HTTP_CRLF "\r\n" // 0x0D0A +#define SRS_HTTP_CRLFCRLF "\r\n\r\n" // 0x0D0A0D0A + +// @see ISrsHttpMessage._http_ts_send_buffer +#define SRS_HTTP_TS_SEND_BUFFER_SIZE 4096 + +// for ead all of http body, read each time. +#define SRS_HTTP_READ_CACHE_BYTES 4096 + +// default http listen port. +#define SRS_DEFAULT_HTTP_PORT 80 + +// for http parser macros +#define SRS_CONSTS_HTTP_OPTIONS HTTP_OPTIONS +#define SRS_CONSTS_HTTP_GET HTTP_GET +#define SRS_CONSTS_HTTP_POST HTTP_POST +#define SRS_CONSTS_HTTP_PUT HTTP_PUT +#define SRS_CONSTS_HTTP_DELETE HTTP_DELETE + +// helper function: response in json format. +extern int srs_http_response_json(ISrsHttpResponseWriter* w, std::string data); + +// get the status text of code. +extern std::string srs_generate_http_status_text(int status); + +// bodyAllowedForStatus reports whether a given response status code +// permits a body. See RFC2616, section 4.4. +extern bool srs_go_http_body_allowd(int status); + +// DetectContentType implements the algorithm described +// at http://mimesniff.spec.whatwg.org/ to determine the +// Content-Type of the given data. It considers at most the +// first 512 bytes of data. DetectContentType always returns +// a valid MIME type: if it cannot determine a more specific one, it +// returns "application/octet-stream". +extern std::string srs_go_http_detect(char* data, int size); + +// state of message +enum SrsHttpParseState { + SrsHttpParseStateInit = 0, + SrsHttpParseStateStart, + SrsHttpParseStateHeaderComplete, + SrsHttpParseStateMessageComplete +}; + +// A Header represents the key-value pairs in an HTTP header. +class SrsHttpHeader +{ +private: + std::map headers; +public: + SrsHttpHeader(); + virtual ~SrsHttpHeader(); +public: + // Add adds the key, value pair to the header. + // It appends to any existing values associated with key. + virtual void set(std::string key, std::string value); + // Get gets the first value associated with the given key. + // If there are no values associated with the key, Get returns "". + // To access multiple values of a key, access the map directly + // with CanonicalHeaderKey. + virtual std::string get(std::string key); +public: + /** + * get the content length. -1 if not set. + */ + virtual int64_t content_length(); + /** + * set the content length by header "Content-Length" + */ + virtual void set_content_length(int64_t size); +public: + /** + * get the content type. empty string if not set. + */ + virtual std::string content_type(); + /** + * set the content type by header "Content-Type" + */ + virtual void set_content_type(std::string ct); +public: + /** + * write all headers to string stream. + */ + virtual void write(std::stringstream& ss); +}; + +// A ResponseWriter interface is used by an HTTP handler to +// construct an HTTP response. +// Usage 1, response with specified length content: +// ISrsHttpResponseWriter* w; // create or get response. +// std::string msg = "Hello, HTTP!"; +// w->header()->set_content_type("text/plain; charset=utf-8"); +// w->header()->set_content_length(msg.length()); +// w->write_header(SRS_CONSTS_HTTP_OK); +// w->write((char*)msg.data(), (int)msg.length()); +// w->final_request(); // optional flush. +// Usage 2, response with HTTP code only, zero content length. +// ISrsHttpResponseWriter* w; // create or get response. +// w->header()->set_content_length(0); +// w->write_header(SRS_CONSTS_HTTP_OK); +// w->final_request(); +// Usage 3, response in chunked encoding. +// ISrsHttpResponseWriter* w; // create or get response. +// std::string msg = "Hello, HTTP!"; +// w->header()->set_content_type("application/octet-stream"); +// w->write_header(SRS_CONSTS_HTTP_OK); +// w->write((char*)msg.data(), (int)msg.length()); +// w->write((char*)msg.data(), (int)msg.length()); +// w->write((char*)msg.data(), (int)msg.length()); +// w->write((char*)msg.data(), (int)msg.length()); +// w->final_request(); // required to end the chunked and flush. +class ISrsHttpResponseWriter +{ +public: + ISrsHttpResponseWriter(); + virtual ~ISrsHttpResponseWriter(); +public: + // when chunked mode, + // final the request to complete the chunked encoding. + // for no-chunked mode, + // final to send request, for example, content-length is 0. + virtual int final_request() = 0; + + // Header returns the header map that will be sent by WriteHeader. + // Changing the header after a call to WriteHeader (or Write) has + // no effect. + virtual SrsHttpHeader* header() = 0; + + // Write writes the data to the connection as part of an HTTP reply. + // If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK) + // before writing the data. If the Header does not contain a + // Content-Type line, Write adds a Content-Type set to the result of passing + // the initial 512 bytes of written data to DetectContentType. + // @param data, the data to send. NULL to flush header only. + virtual int write(char* data, int size) = 0; + + // WriteHeader sends an HTTP response header with status code. + // If WriteHeader is not called explicitly, the first call to Write + // will trigger an implicit WriteHeader(http.StatusOK). + // Thus explicit calls to WriteHeader are mainly used to + // send error codes. + // @remark, user must set header then write or write_header. + virtual void write_header(int code) = 0; +}; + +/** + * the reader interface for http response. + */ +class ISrsHttpResponseReader +{ +public: + ISrsHttpResponseReader(); + virtual ~ISrsHttpResponseReader(); +public: + /** + * whether response read EOF. + */ + virtual bool eof() = 0; + /** + * read from the response body. + * @param data, the buffer to read data buffer to. + * @param nb_data, the max size of data buffer. + * @param nb_read, the actual read size of bytes. NULL to ignore. + * @remark when eof(), return error. + */ + virtual int read(char* data, int nb_data, int* nb_read) = 0; +}; + +// Objects implementing the Handler interface can be +// registered to serve a particular path or subtree +// in the HTTP server. +// +// ServeHTTP should write reply headers and data to the ResponseWriter +// and then return. Returning signals that the request is finished +// and that the HTTP server can move on to the next request on +// the connection. +class ISrsHttpHandler +{ +public: + SrsHttpMuxEntry* entry; +public: + ISrsHttpHandler(); + virtual ~ISrsHttpHandler(); +public: + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) = 0; +}; + +// Redirect to a fixed URL +class SrsHttpRedirectHandler : public ISrsHttpHandler +{ +private: + std::string url; + int code; +public: + SrsHttpRedirectHandler(std::string u, int c); + virtual ~SrsHttpRedirectHandler(); +public: + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); +}; + +// NotFound replies to the request with an HTTP 404 not found error. +class SrsHttpNotFoundHandler : public ISrsHttpHandler +{ +public: + SrsHttpNotFoundHandler(); + virtual ~SrsHttpNotFoundHandler(); +public: + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); +}; + +// FileServer returns a handler that serves HTTP requests +// with the contents of the file system rooted at root. +// +// To use the operating system's file system implementation, +// use http.Dir: +// +// http.Handle("/", SrsHttpFileServer("/tmp")) +// http.Handle("/", SrsHttpFileServer("static-dir")) +class SrsHttpFileServer : public ISrsHttpHandler +{ +protected: + std::string dir; +public: + SrsHttpFileServer(std::string root_dir); + virtual ~SrsHttpFileServer(); +public: + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); +private: + /** + * serve the file by specified path + */ + virtual int serve_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath); + virtual int serve_flv_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath); + virtual int serve_mp4_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath); +protected: + /** + * when access flv file with x.flv?start=xxx + */ + virtual int serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int offset); + /** + * when access mp4 file with x.mp4?range=start-end + * @param start the start offset in bytes. + * @param end the end offset in bytes. -1 to end of file. + * @remark response data in [start, end]. + */ + virtual int serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int start, int end); +protected: + /** + * copy the fs to response writer in size bytes. + */ + virtual int copy(ISrsHttpResponseWriter* w, SrsFileReader* fs, ISrsHttpMessage* r, int size); +}; + +// the mux entry for server mux. +// the matcher info, for example, the pattern and handler. +class SrsHttpMuxEntry +{ +public: + bool explicit_match; + ISrsHttpHandler* handler; + std::string pattern; + bool enabled; +public: + SrsHttpMuxEntry(); + virtual ~SrsHttpMuxEntry(); +}; + +/** + * the hijacker for http pattern match. + */ +class ISrsHttpMatchHijacker +{ +public: + ISrsHttpMatchHijacker(); + virtual ~ISrsHttpMatchHijacker(); +public: + /** + * when match the request failed, no handler to process request. + * @param request the http request message to match the handler. + * @param ph the already matched handler, hijack can rewrite it. + */ + virtual int hijack(ISrsHttpMessage* request, ISrsHttpHandler** ph) = 0; +}; + +// ServeMux is an HTTP request multiplexer. +// It matches the URL of each incoming request against a list of registered +// patterns and calls the handler for the pattern that +// most closely matches the URL. +// +// Patterns name fixed, rooted paths, like "/favicon.ico", +// or rooted subtrees, like "/images/" (note the trailing slash). +// Longer patterns take precedence over shorter ones, so that +// if there are handlers registered for both "/images/" +// and "/images/thumbnails/", the latter handler will be +// called for paths beginning "/images/thumbnails/" and the +// former will receive requests for any other paths in the +// "/images/" subtree. +// +// Note that since a pattern ending in a slash names a rooted subtree, +// the pattern "/" matches all paths not matched by other registered +// patterns, not just the URL with Path == "/". +// +// Patterns may optionally begin with a host name, restricting matches to +// URLs on that host only. Host-specific patterns take precedence over +// general patterns, so that a handler might register for the two patterns +// "/codesearch" and "codesearch.google.com/" without also taking over +// requests for "http://www.google.com/". +// +// ServeMux also takes care of sanitizing the URL request path, +// redirecting any request containing . or .. elements to an +// equivalent .- and ..-free URL. +class SrsHttpServeMux +{ +private: + // the pattern handler, to handle the http request. + std::map entries; + // the vhost handler. + // when find the handler to process the request, + // append the matched vhost when pattern not starts with /, + // for example, for pattern /live/livestream.flv of vhost ossrs.net, + // the path will rewrite to ossrs.net/live/livestream.flv + std::map vhosts; + // all hijackers for http match. + // for example, the hstrs(http stream trigger rtmp source) + // can hijack and install handler when request incoming and no handler. + std::vector hijackers; +public: + SrsHttpServeMux(); + virtual ~SrsHttpServeMux(); +public: + /** + * initialize the http serve mux. + */ + virtual int initialize(); + /** + * hijack the http match. + */ + virtual void hijack(ISrsHttpMatchHijacker* h); + virtual void unhijack(ISrsHttpMatchHijacker* h); +public: + // Handle registers the handler for the given pattern. + // If a handler already exists for pattern, Handle panics. + virtual int handle(std::string pattern, ISrsHttpHandler* handler); + // interface ISrsHttpHandler +public: + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); +private: + virtual int find_handler(ISrsHttpMessage* r, ISrsHttpHandler** ph); + virtual int match(ISrsHttpMessage* r, ISrsHttpHandler** ph); + virtual bool path_match(std::string pattern, std::string path); +}; + +// for http header. +typedef std::pair SrsHttpHeaderField; + +// A Request represents an HTTP request received by a server +// or to be sent by a client. +// +// The field semantics differ slightly between client and server +// usage. In addition to the notes on the fields below, see the +// documentation for Request.Write and RoundTripper. +/** + * the http message, request or response. + */ +class ISrsHttpMessage +{ +private: + /** + * use a buffer to read and send ts file. + */ + // TODO: FIXME: remove it. + char* _http_ts_send_buffer; +public: + ISrsHttpMessage(); + virtual ~ISrsHttpMessage(); +public: + /** + * the http request level cache. + */ + virtual char* http_ts_send_buffer(); +public: + virtual u_int8_t method() = 0; + virtual u_int16_t status_code() = 0; + /** + * method helpers. + */ + virtual std::string method_str() = 0; + virtual bool is_http_get() = 0; + virtual bool is_http_put() = 0; + virtual bool is_http_post() = 0; + virtual bool is_http_delete() = 0; + virtual bool is_http_options() = 0; +public: + /** + * whether should keep the connection alive. + */ + virtual bool is_keep_alive() = 0; + /** + * the uri contains the host and path. + */ + virtual std::string uri() = 0; + /** + * the url maybe the path. + */ + virtual std::string url() = 0; + virtual std::string host() = 0; + virtual std::string path() = 0; + virtual std::string ext() = 0; +public: + /** + * read body to string. + * @remark for small http body. + */ + virtual int body_read_all(std::string& body) = 0; + /** + * get the body reader, to read one by one. + * @remark when body is very large, or chunked, use this. + */ + virtual ISrsHttpResponseReader* body_reader() = 0; + /** + * the content length, -1 for chunked or not set. + */ + virtual int64_t content_length() = 0; +public: + /** + * get the param in query string, + * for instance, query is "start=100&end=200", + * then query_get("start") is "100", and query_get("end") is "200" + */ + virtual std::string query_get(std::string key) = 0; + /** + * get the headers. + */ + virtual int request_header_count() = 0; + virtual std::string request_header_key_at(int index) = 0; + virtual std::string request_header_value_at(int index) = 0; +}; + #endif