From 9bbbaad2880135f9a97fbea56b8b63555005a7e0 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 17 Jan 2015 21:58:23 +0800 Subject: [PATCH] for bug #277, refine http api refer to go http-framework. 2.0.97 --- README.md | 1 + trunk/src/app/srs_app_http.cpp | 586 ++++++++++++++++++++++++-- trunk/src/app/srs_app_http.hpp | 234 +++++++++- trunk/src/app/srs_app_http_api.cpp | 362 ++++++---------- trunk/src/app/srs_app_http_api.hpp | 138 +++--- trunk/src/app/srs_app_http_conn.cpp | 5 - trunk/src/app/srs_app_server.cpp | 60 ++- trunk/src/app/srs_app_server.hpp | 3 +- trunk/src/core/srs_core.hpp | 2 +- trunk/src/kernel/srs_kernel_error.hpp | 8 + 10 files changed, 1004 insertions(+), 395 deletions(-) diff --git a/README.md b/README.md index e66803832..240bc861f 100755 --- a/README.md +++ b/README.md @@ -510,6 +510,7 @@ Supported operating systems and hardware: ## History +* v2.0, 2015-01-17, for [#277](https://github.com/winlinvip/simple-rtmp-server/issues/277), refine http api refer to go http-framework. 2.0.97 * v2.0, 2015-01-17, hotfix [#290](https://github.com/winlinvip/simple-rtmp-server/issues/290), use iformat only for rtmp input. 2.0.95 * v2.0, 2015-01-08, hotfix [#281](https://github.com/winlinvip/simple-rtmp-server/issues/281), fix hls bug ignore type-9 send aud. 2.0.93 * v2.0, 2015-01-03, fix [#274](https://github.com/winlinvip/simple-rtmp-server/issues/274), http-callback support on_dvr when reap a dvr file. 2.0.89 diff --git a/trunk/src/app/srs_app_http.cpp b/trunk/src/app/srs_app_http.cpp index c6d542ba0..838c30b41 100644 --- a/trunk/src/app/srs_app_http.cpp +++ b/trunk/src/app/srs_app_http.cpp @@ -68,6 +68,503 @@ bool srs_path_like(const char* expect, const char* path, int nb_path) return equals; } +int srs_go_http_response_json(ISrsGoHttpResponseWriter* w, string data) +{ + w->header()->set_content_length(data.length()); + w->header()->set_content_type("application/json;charset=utf-8"); + + return w->write((char*)data.data(), data.length()); +} + +// get the status text of code. +string srs_generate_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) +{ + 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(ISrsGoHttpResponseWriter* 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; +} + +SrsGoHttpHeader::SrsGoHttpHeader() +{ +} + +SrsGoHttpHeader::~SrsGoHttpHeader() +{ +} + +void SrsGoHttpHeader::set(string key, string value) +{ + headers[key] = value; +} + +string SrsGoHttpHeader::get(string key) +{ + std::string v; + + if (headers.find(key) != headers.end()) { + v = headers[key]; + } + + return v; +} + +int64_t SrsGoHttpHeader::content_length() +{ + std::string cl = get("Content-Length"); + + if (cl.empty()) { + return -1; + } + + return (int64_t)::atof(cl.c_str()); +} + +void SrsGoHttpHeader::set_content_length(int64_t size) +{ + char buf[64]; + snprintf(buf, sizeof(buf), "%"PRId64, size); + set("Content-Length", buf); +} + +string SrsGoHttpHeader::content_type() +{ + return get("Content-Type"); +} + +void SrsGoHttpHeader::set_content_type(string ct) +{ + set("Content-Type", ct); +} + +void SrsGoHttpHeader::write(stringstream& ss) +{ + std::map::iterator it; + for (it = headers.begin(); it != headers.end(); ++it) { + ss << it->first << ": " << it->second << __SRS_CRLF; + } +} + +ISrsGoHttpResponseWriter::ISrsGoHttpResponseWriter() +{ +} + +ISrsGoHttpResponseWriter::~ISrsGoHttpResponseWriter() +{ +} + +ISrsGoHttpHandler::ISrsGoHttpHandler() +{ +} + +ISrsGoHttpHandler::~ISrsGoHttpHandler() +{ +} + +SrsGoHttpRedirectHandler::SrsGoHttpRedirectHandler(string u, int c) +{ + url = u; + code = c; +} + +SrsGoHttpRedirectHandler::~SrsGoHttpRedirectHandler() +{ +} + +int SrsGoHttpRedirectHandler::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) +{ + int ret = ERROR_SUCCESS; + // TODO: FIXME: implements it. + return ret; +} + +SrsGoHttpNotFoundHandler::SrsGoHttpNotFoundHandler() +{ +} + +SrsGoHttpNotFoundHandler::~SrsGoHttpNotFoundHandler() +{ +} + +int SrsGoHttpNotFoundHandler::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) +{ + return srs_go_http_error(w, + SRS_CONSTS_HTTP_NotFound, SRS_CONSTS_HTTP_NotFound_str); +} + +SrsGoHttpMuxEntry::SrsGoHttpMuxEntry() +{ + explicit_match = false; + handler = NULL; +} + +SrsGoHttpMuxEntry::~SrsGoHttpMuxEntry() +{ + srs_freep(handler); +} + +SrsGoHttpServeMux::SrsGoHttpServeMux() +{ +} + +SrsGoHttpServeMux::~SrsGoHttpServeMux() +{ + std::map::iterator it; + for (it = entries.begin(); it != entries.end(); ++it) { + SrsGoHttpMuxEntry* entry = it->second; + srs_freep(entry); + } + entries.clear(); +} + +int SrsGoHttpServeMux::initialize() +{ + int ret = ERROR_SUCCESS; + // TODO: FIXME: implements it. + return ret; +} + +int SrsGoHttpServeMux::handle(std::string pattern, ISrsGoHttpHandler* 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()) { + SrsGoHttpMuxEntry* 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; + } + } + + if (true) { + SrsGoHttpMuxEntry* entry = new SrsGoHttpMuxEntry(); + entry->explicit_match = true; + entry->handler = handler; + entry->pattern = pattern; + + if (entries.find(pattern) != entries.end()) { + SrsGoHttpMuxEntry* 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.empty() && pattern.at(pattern.length() - 1) == '/') { + std::string rpattern = pattern.substr(0, pattern.length() - 1); + SrsGoHttpMuxEntry* entry = NULL; + + // free the exists not explicit entry + if (entries.find(rpattern) != entries.end()) { + SrsGoHttpMuxEntry* exists = entries[rpattern]; + if (!exists->explicit_match) { + entry = exists; + } + } + + // create implicit redirect. + if (!entry || entry->explicit_match) { + srs_freep(entry); + + entry = new SrsGoHttpMuxEntry(); + entry->explicit_match = false; + entry->handler = new SrsGoHttpRedirectHandler(pattern, SRS_CONSTS_HTTP_MovedPermanently); + entry->pattern = pattern; + + entries[rpattern] = entry; + } + } + + return ret; +} + +int SrsGoHttpServeMux::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) +{ + int ret = ERROR_SUCCESS; + + ISrsGoHttpHandler* 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) { + srs_error("handler serve http failed. ret=%d", ret); + return ret; + } + + return ret; +} + +int SrsGoHttpServeMux::find_handler(SrsHttpMessage* r, ISrsGoHttpHandler** 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; + } + + if (*ph == NULL) { + *ph = new SrsGoHttpNotFoundHandler(); + } + + return ret; +} + +int SrsGoHttpServeMux::match(SrsHttpMessage* r, ISrsGoHttpHandler** ph) +{ + int ret = ERROR_SUCCESS; + + std::string path = r->path(); + + int nb_matched = 0; + ISrsGoHttpHandler* h = NULL; + + std::map::iterator it; + for (it = entries.begin(); it != entries.end(); ++it) { + std::string pattern = it->first; + SrsGoHttpMuxEntry* entry = it->second; + + 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 SrsGoHttpServeMux::path_match(string pattern, string path) +{ + if (pattern.empty()) { + return false; + } + + int n = 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; +} + +SrsGoHttpResponseWriter::SrsGoHttpResponseWriter(SrsStSocket* io) +{ + skt = io; + hdr = new SrsGoHttpHeader(); + header_wrote = false; + status = SRS_CONSTS_HTTP_OK; + content_length = -1; + written = 0; + header_sent = false; +} + +SrsGoHttpResponseWriter::~SrsGoHttpResponseWriter() +{ + srs_freep(hdr); +} + +SrsGoHttpHeader* SrsGoHttpResponseWriter::header() +{ + return hdr; +} + +int SrsGoHttpResponseWriter::write(char* data, int size) +{ + int ret = ERROR_SUCCESS; + + if (!header_wrote) { + write_header(SRS_CONSTS_HTTP_OK); + } + + written += size; + if (content_length != -1 && written > content_length) { + ret = ERROR_HTTP_CONTENT_LENGTH; + srs_error("http: exceed content length. ret=%d", ret); + return ret; + } + + if ((ret = send_header(data, size)) != ERROR_SUCCESS) { + srs_error("http: send header failed. ret=%d", ret); + return ret; + } + + return skt->write((void*)data, size, NULL); +} + +void SrsGoHttpResponseWriter::write_header(int code) +{ + if (header_wrote) { + srs_warn("http: multiple write_header calls, code=%d", code); + return; + } + + header_wrote = true; + status = code; + + // parse the content length from header. + content_length = hdr->content_length(); +} + +int SrsGoHttpResponseWriter::send_header(char* data, int size) +{ + int ret = ERROR_SUCCESS; + + if (header_sent) { + return ret; + } + header_sent = true; + + std::stringstream ss; + + // status_line + ss << "HTTP/1.1 " << status << " " + << srs_generate_status_text(status) << __SRS_CRLF; + + // detect content type + if (srs_go_http_body_allowd(status)) { + if (hdr->content_type().empty()) { + hdr->set_content_type(srs_go_http_detect(data, size)); + } + } + + // set server if not set. + if (hdr->get("Server").empty()) { + hdr->set("Server", RTMP_SIG_SRS_KEY"/"RTMP_SIG_SRS_VERSION); + } + + // write headers + hdr->write(ss); + + // header_eof + ss << __SRS_CRLF; + + std::string buf = ss.str(); + return skt->write((void*)buf.c_str(), buf.length(), NULL); +} + SrsHttpHandlerMatch::SrsHttpHandlerMatch() { handler = NULL; @@ -521,13 +1018,6 @@ int SrsHttpHandler::res_error(SrsStSocket* skt, SrsHttpMessage* req, int code, s return res_flush(skt, ss); } -#ifdef SRS_AUTO_HTTP_API -SrsHttpHandler* SrsHttpHandler::create_http_api() -{ - return new SrsApiRoot(); -} -#endif - #ifdef SRS_AUTO_HTTP_SERVER SrsHttpHandler* SrsHttpHandler::create_http_stream() { @@ -553,6 +1043,41 @@ SrsHttpMessage::~SrsHttpMessage() srs_freep(_http_ts_send_buffer); } +int SrsHttpMessage::initialize() +{ + int ret = ERROR_SUCCESS; + + // parse uri to schema/server:port/path?query + if ((ret = _uri->initialize(_url)) != ERROR_SUCCESS) { + return ret; + } + + // must format as key=value&...&keyN=valueN + std::string q = _uri->get_query(); + size_t pos = string::npos; + while (!q.empty()) { + std::string k = q; + if ((pos = q.find("=")) != string::npos) { + k = q.substr(0, pos); + q = q.substr(pos + 1); + } else { + q = ""; + } + + std::string v = q; + if ((pos = q.find("&")) != string::npos) { + v = q.substr(0, pos); + q = q.substr(pos + 1); + } else { + q = ""; + } + + _query[k] = v; + } + + return ret; +} + char* SrsHttpMessage::http_ts_send_buffer() { return _http_ts_send_buffer; @@ -565,24 +1090,6 @@ void SrsHttpMessage::reset() _url = ""; } -int SrsHttpMessage::parse_uri() -{ - // filter url according to HTTP specification. - - // remove the duplicated slash. - std::string filtered_url = srs_string_replace(_url, "//", "/"); - - // remove the last / to match resource. - filtered_url = srs_string_trim_end(filtered_url, "/"); - - // if empty, use root. - if (filtered_url.empty()) { - filtered_url = "/"; - } - - return _uri->initialize(filtered_url); -} - bool SrsHttpMessage::is_complete() { return _state == SrsHttpParseStateComplete; @@ -671,11 +1178,6 @@ string SrsHttpMessage::path() return _uri->get_path(); } -string SrsHttpMessage::query() -{ - return _uri->get_query(); -} - string SrsHttpMessage::body() { std::string b; @@ -745,21 +1247,10 @@ void SrsHttpMessage::append_body(const char* body, int length) string SrsHttpMessage::query_get(string key) { - std::string q = query(); - size_t pos = std::string::npos; + std::string v; - // must format as key=value&...&keyN=valueN - if ((pos = key.find("=")) != key.length() - 1) { - key = key + "="; - } - - if ((pos = q.find(key)) == std::string::npos) { - return ""; - } - - std::string v = q.substr(pos + key.length()); - if ((pos = v.find("&")) != std::string::npos) { - v = v.substr(0, pos); + if (_query.find(key) != _query.end()) { + v = _query[key]; } return v; @@ -859,6 +1350,13 @@ int SrsHttpParser::parse_message(SrsStSocket* skt, SrsHttpMessage** ppmsg) srs_freep(msg); return ret; } + + // initalize http msg, parse url. + if ((ret = msg->initialize()) != ERROR_SUCCESS) { + srs_error("initialize http msg failed. ret=%d", ret); + srs_freep(msg); + return ret; + } // parse ok, return the msg. *ppmsg = msg; diff --git a/trunk/src/app/srs_app_http.hpp b/trunk/src/app/srs_app_http.hpp index 7ea2ec7b1..0113f7fb5 100644 --- a/trunk/src/app/srs_app_http.hpp +++ b/trunk/src/app/srs_app_http.hpp @@ -31,6 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifdef SRS_AUTO_HTTP_PARSER +#include #include #include #include @@ -45,6 +46,7 @@ class SrsStSocket; class SrsHttpUri; class SrsHttpMessage; class SrsHttpHandler; +class ISrsGoHttpResponseWriter; // http specification // CR = @@ -65,6 +67,9 @@ class SrsHttpHandler; // @see SrsHttpMessage._http_ts_send_buffer #define __SRS_HTTP_TS_SEND_BUFFER_SIZE 4096 +// helper function: response in json format. +extern int srs_go_http_response_json(ISrsGoHttpResponseWriter* w, std::string data); + // compare the path. // full compare, extractly match. // used for api match. @@ -81,6 +86,214 @@ enum SrsHttpParseState { SrsHttpParseStateComplete }; +// A Header represents the key-value pairs in an HTTP header. +class SrsGoHttpHeader +{ +private: + std::map headers; +public: + SrsGoHttpHeader(); + virtual ~SrsGoHttpHeader(); +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. +class ISrsGoHttpResponseWriter +{ +public: + ISrsGoHttpResponseWriter(); + virtual ~ISrsGoHttpResponseWriter(); +public: + // 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 SrsGoHttpHeader* 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. + 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. + virtual void write_header(int code) = 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 ISrsGoHttpHandler +{ +public: + ISrsGoHttpHandler(); + virtual ~ISrsGoHttpHandler(); +public: + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) = 0; +}; + +// Redirect to a fixed URL +class SrsGoHttpRedirectHandler : public ISrsGoHttpHandler +{ +private: + std::string url; + int code; +public: + SrsGoHttpRedirectHandler(std::string u, int c); + virtual ~SrsGoHttpRedirectHandler(); +public: + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); +}; + +// NotFound replies to the request with an HTTP 404 not found error. +class SrsGoHttpNotFoundHandler : public ISrsGoHttpHandler +{ +public: + SrsGoHttpNotFoundHandler(); + virtual ~SrsGoHttpNotFoundHandler(); +public: + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); +}; + +// the mux entry for server mux. +class SrsGoHttpMuxEntry +{ +public: + bool explicit_match; + ISrsGoHttpHandler* handler; + std::string pattern; +public: + SrsGoHttpMuxEntry(); + virtual ~SrsGoHttpMuxEntry(); +}; + +// 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 SrsGoHttpServeMux +{ +private: + std::map entries; +public: + SrsGoHttpServeMux(); + virtual ~SrsGoHttpServeMux(); +public: + /** + * initialize the http serve mux. + */ + virtual int initialize(); +public: + // Handle registers the handler for the given pattern. + // If a handler already exists for pattern, Handle panics. + virtual int handle(std::string pattern, ISrsGoHttpHandler* handler); +// interface ISrsGoHttpHandler +public: + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); +private: + virtual int find_handler(SrsHttpMessage* r, ISrsGoHttpHandler** ph); + virtual int match(SrsHttpMessage* r, ISrsGoHttpHandler** ph); + virtual bool path_match(std::string pattern, std::string path); +}; + +/** +* response writer use st socket +*/ +class SrsGoHttpResponseWriter : public ISrsGoHttpResponseWriter +{ +private: + SrsStSocket* skt; + SrsGoHttpHeader* hdr; +private: + // reply header has been (logically) written + bool header_wrote; + // status code passed to WriteHeader + int status; +private: + // explicitly-declared Content-Length; or -1 + int64_t content_length; + // number of bytes written in body + int64_t written; +private: + // wroteHeader tells whether the header's been written to "the + // wire" (or rather: w.conn.buf). this is unlike + // (*response).wroteHeader, which tells only whether it was + // logically written. + bool header_sent; +public: + SrsGoHttpResponseWriter(SrsStSocket* io); + virtual ~SrsGoHttpResponseWriter(); +public: + virtual SrsGoHttpHeader* header(); + virtual int write(char* data, int size); + virtual void write_header(int code); + virtual int send_header(char* data, int size); +}; + /** * the matched handler info. */ @@ -182,12 +395,6 @@ public: virtual int res_error(SrsStSocket* skt, SrsHttpMessage* req, int code, std::string reason_phrase, std::string body); // object creator public: - /** - * create http api resource handler. - */ -#ifdef SRS_AUTO_HTTP_API - static SrsHttpHandler* create_http_api(); -#endif /** * create http stream resource handler. */ @@ -196,6 +403,12 @@ public: #endif }; +// 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. */ @@ -239,13 +452,16 @@ private: // http headers typedef std::pair SrsHttpHeaderField; std::vector headers; + // the query map + std::map _query; public: SrsHttpMessage(); virtual ~SrsHttpMessage(); +public: + virtual int initialize(); public: virtual char* http_ts_send_buffer(); virtual void reset(); - virtual int parse_uri(); public: virtual bool is_complete(); virtual u_int8_t method(); @@ -260,7 +476,7 @@ public: virtual std::string url(); virtual std::string host(); virtual std::string path(); - virtual std::string query(); +public: virtual std::string body(); virtual char* body_raw(); virtual int64_t body_size(); @@ -273,14 +489,12 @@ public: virtual void set_match(SrsHttpHandlerMatch* match); virtual void set_requires_crossdomain(bool requires_crossdomain); virtual void append_body(const char* body, int length); -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); -public: virtual int request_header_count(); virtual std::string request_header_key_at(int index); virtual std::string request_header_value_at(int index); diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 10f932f1c..3edeafa72 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -38,41 +38,15 @@ using namespace std; #include #include -SrsApiRoot::SrsApiRoot() +SrsGoApiRoot::SrsGoApiRoot() { - handlers.push_back(new SrsApiApi()); } -SrsApiRoot::~SrsApiRoot() +SrsGoApiRoot::~SrsGoApiRoot() { } -bool SrsApiRoot::is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase) -{ - if (!SrsHttpHandler::is_handler_valid(req, status_code, reason_phrase)) { - return false; - } - - if (req->match()->matched_url.length() != 1) { - status_code = SRS_CONSTS_HTTP_NotFound; - reason_phrase = SRS_CONSTS_HTTP_NotFound_str; - return false; - } - - return true; -} - -bool SrsApiRoot::can_handle(const char* path, int /*length*/, const char** pchild) -{ - // reset the child path to path, - // for child to reparse the path. - *pchild = path; - - // only compare the first char. - return srs_path_equals("/", path, 1); -} - -int SrsApiRoot::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) +int SrsGoApiRoot::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) { std::stringstream ss; @@ -82,25 +56,19 @@ int SrsApiRoot::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) << __SRS_JFIELD_STR("api", "the api root") << __SRS_JOBJECT_END << __SRS_JOBJECT_END; - - return res_json(skt, req, ss.str()); -} - -SrsApiApi::SrsApiApi() -{ - handlers.push_back(new SrsApiV1()); + + return srs_go_http_response_json(w, ss.str()); } -SrsApiApi::~SrsApiApi() +SrsGoApiApi::SrsGoApiApi() { } -bool SrsApiApi::can_handle(const char* path, int length, const char** /*pchild*/) +SrsGoApiApi::~SrsGoApiApi() { - return srs_path_equals("/api", path, length); } -int SrsApiApi::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) +int SrsGoApiApi::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) { std::stringstream ss; @@ -110,34 +78,19 @@ int SrsApiApi::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) << __SRS_JFIELD_STR("v1", "the api version 1.0") << __SRS_JOBJECT_END << __SRS_JOBJECT_END; - - return res_json(skt, req, ss.str()); -} - -SrsApiV1::SrsApiV1() -{ - handlers.push_back(new SrsApiVersion()); - handlers.push_back(new SrsApiSummaries()); - handlers.push_back(new SrsApiRusages()); - handlers.push_back(new SrsApiSelfProcStats()); - handlers.push_back(new SrsApiSystemProcStats()); - handlers.push_back(new SrsApiMemInfos()); - handlers.push_back(new SrsApiAuthors()); - handlers.push_back(new SrsApiRequests()); - handlers.push_back(new SrsApiVhosts()); - handlers.push_back(new SrsApiStreams()); + + return srs_go_http_response_json(w, ss.str()); } -SrsApiV1::~SrsApiV1() +SrsGoApiV1::SrsGoApiV1() { } -bool SrsApiV1::can_handle(const char* path, int length, const char** /*pchild*/) +SrsGoApiV1::~SrsGoApiV1() { - return srs_path_equals("/v1", path, length); } -int SrsApiV1::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) +int SrsGoApiV1::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) { std::stringstream ss; @@ -157,87 +110,18 @@ int SrsApiV1::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) << __SRS_JOBJECT_END << __SRS_JOBJECT_END; - return res_json(skt, req, ss.str()); + return srs_go_http_response_json(w, ss.str()); } -SrsApiRequests::SrsApiRequests() +SrsGoApiVersion::SrsGoApiVersion() { } -SrsApiRequests::~SrsApiRequests() +SrsGoApiVersion::~SrsGoApiVersion() { } -bool SrsApiRequests::can_handle(const char* path, int length, const char** /*pchild*/) -{ - return srs_path_equals("/requests", path, length); -} - -int SrsApiRequests::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) -{ - std::stringstream ss; - - ss << __SRS_JOBJECT_START - << __SRS_JFIELD_ERROR(ERROR_SUCCESS) << __SRS_JFIELD_CONT - << __SRS_JFIELD_ORG("data", __SRS_JOBJECT_START) - << __SRS_JFIELD_STR("uri", req->uri()) << __SRS_JFIELD_CONT - << __SRS_JFIELD_STR("path", req->path()) << __SRS_JFIELD_CONT; - - // method - if (req->is_http_get()) { - ss << __SRS_JFIELD_STR("METHOD", "GET"); - } else if (req->is_http_post()) { - ss << __SRS_JFIELD_STR("METHOD", "POST"); - } else if (req->is_http_put()) { - ss << __SRS_JFIELD_STR("METHOD", "PUT"); - } else if (req->is_http_delete()) { - ss << __SRS_JFIELD_STR("METHOD", "DELETE"); - } else { - ss << __SRS_JFIELD_ORG("METHOD", req->method()); - } - ss << __SRS_JFIELD_CONT; - - // request headers - ss << __SRS_JFIELD_NAME("headers") << __SRS_JOBJECT_START; - for (int i = 0; i < req->request_header_count(); i++) { - std::string key = req->request_header_key_at(i); - std::string value = req->request_header_value_at(i); - if ( i < req->request_header_count() - 1) { - ss << __SRS_JFIELD_STR(key, value) << __SRS_JFIELD_CONT; - } else { - ss << __SRS_JFIELD_STR(key, value); - } - } - ss << __SRS_JOBJECT_END << __SRS_JFIELD_CONT; - - // server informations - ss << __SRS_JFIELD_NAME("server") << __SRS_JOBJECT_START - << __SRS_JFIELD_STR("sigature", RTMP_SIG_SRS_KEY) << __SRS_JFIELD_CONT - << __SRS_JFIELD_STR("name", RTMP_SIG_SRS_NAME) << __SRS_JFIELD_CONT - << __SRS_JFIELD_STR("version", RTMP_SIG_SRS_VERSION) << __SRS_JFIELD_CONT - << __SRS_JFIELD_STR("link", RTMP_SIG_SRS_URL) << __SRS_JFIELD_CONT - << __SRS_JFIELD_ORG("time", srs_get_system_time_ms()) - << __SRS_JOBJECT_END - << __SRS_JOBJECT_END - << __SRS_JOBJECT_END; - - return res_json(skt, req, ss.str()); -} - -SrsApiVersion::SrsApiVersion() -{ -} - -SrsApiVersion::~SrsApiVersion() -{ -} - -bool SrsApiVersion::can_handle(const char* path, int length, const char** /*pchild*/) -{ - return srs_path_equals("/versions", path, length); -} - -int SrsApiVersion::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) +int SrsGoApiVersion::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) { std::stringstream ss; @@ -251,43 +135,33 @@ int SrsApiVersion::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) << __SRS_JOBJECT_END << __SRS_JOBJECT_END; - return res_json(skt, req, ss.str()); -} - -SrsApiSummaries::SrsApiSummaries() -{ + return srs_go_http_response_json(w, ss.str()); } -SrsApiSummaries::~SrsApiSummaries() +SrsGoApiSummaries::SrsGoApiSummaries() { } -bool SrsApiSummaries::can_handle(const char* path, int length, const char** /*pchild*/) +SrsGoApiSummaries::~SrsGoApiSummaries() { - return srs_path_equals("/summaries", path, length); } -int SrsApiSummaries::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) +int SrsGoApiSummaries::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) { std::stringstream ss; srs_api_dump_summaries(ss); - return res_json(skt, req, ss.str()); + return srs_go_http_response_json(w, ss.str()); } -SrsApiRusages::SrsApiRusages() +SrsGoApiRusages::SrsGoApiRusages() { } -SrsApiRusages::~SrsApiRusages() +SrsGoApiRusages::~SrsGoApiRusages() { } -bool SrsApiRusages::can_handle(const char* path, int length, const char** /*pchild*/) -{ - return srs_path_equals("/rusages", path, length); -} - -int SrsApiRusages::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) +int SrsGoApiRusages::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* req) { std::stringstream ss; @@ -317,23 +191,18 @@ int SrsApiRusages::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) << __SRS_JOBJECT_END << __SRS_JOBJECT_END; - return res_json(skt, req, ss.str()); -} - -SrsApiSelfProcStats::SrsApiSelfProcStats() -{ + return srs_go_http_response_json(w, ss.str()); } -SrsApiSelfProcStats::~SrsApiSelfProcStats() +SrsGoApiSelfProcStats::SrsGoApiSelfProcStats() { } -bool SrsApiSelfProcStats::can_handle(const char* path, int length, const char** /*pchild*/) +SrsGoApiSelfProcStats::~SrsGoApiSelfProcStats() { - return srs_path_equals("/self_proc_stats", path, length); } -int SrsApiSelfProcStats::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) +int SrsGoApiSelfProcStats::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) { std::stringstream ss; @@ -392,23 +261,18 @@ int SrsApiSelfProcStats::do_process_request(SrsStSocket* skt, SrsHttpMessage* re << __SRS_JOBJECT_END << __SRS_JOBJECT_END; - return res_json(skt, req, ss.str()); + return srs_go_http_response_json(w, ss.str()); } -SrsApiSystemProcStats::SrsApiSystemProcStats() +SrsGoApiSystemProcStats::SrsGoApiSystemProcStats() { } -SrsApiSystemProcStats::~SrsApiSystemProcStats() +SrsGoApiSystemProcStats::~SrsGoApiSystemProcStats() { } -bool SrsApiSystemProcStats::can_handle(const char* path, int length, const char** /*pchild*/) -{ - return srs_path_equals("/system_proc_stats", path, length); -} - -int SrsApiSystemProcStats::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) +int SrsGoApiSystemProcStats::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) { std::stringstream ss; @@ -432,23 +296,18 @@ int SrsApiSystemProcStats::do_process_request(SrsStSocket* skt, SrsHttpMessage* << __SRS_JOBJECT_END << __SRS_JOBJECT_END; - return res_json(skt, req, ss.str()); + return srs_go_http_response_json(w, ss.str()); } -SrsApiMemInfos::SrsApiMemInfos() +SrsGoApiMemInfos::SrsGoApiMemInfos() { } -SrsApiMemInfos::~SrsApiMemInfos() +SrsGoApiMemInfos::~SrsGoApiMemInfos() { } -bool SrsApiMemInfos::can_handle(const char* path, int length, const char** /*pchild*/) -{ - return srs_path_equals("/meminfos", path, length); -} - -int SrsApiMemInfos::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) +int SrsGoApiMemInfos::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) { std::stringstream ss; @@ -473,23 +332,18 @@ int SrsApiMemInfos::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) << __SRS_JOBJECT_END << __SRS_JOBJECT_END; - return res_json(skt, req, ss.str()); + return srs_go_http_response_json(w, ss.str()); } -SrsApiAuthors::SrsApiAuthors() +SrsGoApiAuthors::SrsGoApiAuthors() { } -SrsApiAuthors::~SrsApiAuthors() +SrsGoApiAuthors::~SrsGoApiAuthors() { } -bool SrsApiAuthors::can_handle(const char* path, int length, const char** /*pchild*/) -{ - return srs_path_equals("/authors", path, length); -} - -int SrsApiAuthors::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) +int SrsGoApiAuthors::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) { std::stringstream ss; @@ -503,24 +357,82 @@ int SrsApiAuthors::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) << __SRS_JOBJECT_END << __SRS_JOBJECT_END; - return res_json(skt, req, ss.str()); + return srs_go_http_response_json(w, ss.str()); +} + +SrsGoApiRequests::SrsGoApiRequests() +{ +} + +SrsGoApiRequests::~SrsGoApiRequests() +{ } -SrsApiVhosts::SrsApiVhosts() +int SrsGoApiRequests::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) { + SrsHttpMessage* req = r; + + std::stringstream ss; + + ss << __SRS_JOBJECT_START + << __SRS_JFIELD_ERROR(ERROR_SUCCESS) << __SRS_JFIELD_CONT + << __SRS_JFIELD_ORG("data", __SRS_JOBJECT_START) + << __SRS_JFIELD_STR("uri", req->uri()) << __SRS_JFIELD_CONT + << __SRS_JFIELD_STR("path", req->path()) << __SRS_JFIELD_CONT; + + // method + if (req->is_http_get()) { + ss << __SRS_JFIELD_STR("METHOD", "GET"); + } else if (req->is_http_post()) { + ss << __SRS_JFIELD_STR("METHOD", "POST"); + } else if (req->is_http_put()) { + ss << __SRS_JFIELD_STR("METHOD", "PUT"); + } else if (req->is_http_delete()) { + ss << __SRS_JFIELD_STR("METHOD", "DELETE"); + } else { + ss << __SRS_JFIELD_ORG("METHOD", req->method()); + } + ss << __SRS_JFIELD_CONT; + + // request headers + ss << __SRS_JFIELD_NAME("headers") << __SRS_JOBJECT_START; + for (int i = 0; i < req->request_header_count(); i++) { + std::string key = req->request_header_key_at(i); + std::string value = req->request_header_value_at(i); + if ( i < req->request_header_count() - 1) { + ss << __SRS_JFIELD_STR(key, value) << __SRS_JFIELD_CONT; + } else { + ss << __SRS_JFIELD_STR(key, value); + } + } + ss << __SRS_JOBJECT_END << __SRS_JFIELD_CONT; + + // server informations + ss << __SRS_JFIELD_NAME("server") << __SRS_JOBJECT_START + << __SRS_JFIELD_STR("sigature", RTMP_SIG_SRS_KEY) << __SRS_JFIELD_CONT + << __SRS_JFIELD_STR("name", RTMP_SIG_SRS_NAME) << __SRS_JFIELD_CONT + << __SRS_JFIELD_STR("version", RTMP_SIG_SRS_VERSION) << __SRS_JFIELD_CONT + << __SRS_JFIELD_STR("link", RTMP_SIG_SRS_URL) << __SRS_JFIELD_CONT + << __SRS_JFIELD_ORG("time", srs_get_system_time_ms()) + << __SRS_JOBJECT_END + << __SRS_JOBJECT_END + << __SRS_JOBJECT_END; + + return srs_go_http_response_json(w, ss.str()); } -SrsApiVhosts::~SrsApiVhosts() +SrsGoApiVhosts::SrsGoApiVhosts() { } -bool SrsApiVhosts::can_handle(const char* path, int length, const char** /*pchild*/) +SrsGoApiVhosts::~SrsGoApiVhosts() { - return srs_path_equals("/vhosts", path, length); } -int SrsApiVhosts::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) +int SrsGoApiVhosts::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) { + SrsHttpMessage* req = r; + std::stringstream data; SrsStatistic* stat = SrsStatistic::instance(); int ret = stat->dumps_vhosts(data); @@ -533,23 +445,18 @@ int SrsApiVhosts::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) << __SRS_JFIELD_ORG("vhosts", data.str()) << __SRS_JOBJECT_END; - return res_json(skt, req, ss.str()); + return srs_go_http_response_json(w, ss.str()); } -SrsApiStreams::SrsApiStreams() +SrsGoApiStreams::SrsGoApiStreams() { } -SrsApiStreams::~SrsApiStreams() +SrsGoApiStreams::~SrsGoApiStreams() { } -bool SrsApiStreams::can_handle(const char* path, int length, const char** /*pchild*/) -{ - return srs_path_equals("/streams", path, length); -} - -int SrsApiStreams::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) +int SrsGoApiStreams::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) { std::stringstream data; SrsStatistic* stat = SrsStatistic::instance(); @@ -563,15 +470,14 @@ int SrsApiStreams::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) << __SRS_JFIELD_ORG("streams", data.str()) << __SRS_JOBJECT_END; - return res_json(skt, req, ss.str()); + return srs_go_http_response_json(w, ss.str()); } -SrsHttpApi::SrsHttpApi(SrsServer* srs_server, st_netfd_t client_stfd, SrsHttpHandler* _handler) - : SrsConnection(srs_server, client_stfd) +SrsHttpApi::SrsHttpApi(SrsServer* svr, st_netfd_t fd, SrsGoHttpServeMux* m) + : SrsConnection(svr, fd) { + mux = m; parser = new SrsHttpParser(); - handler = _handler; - requires_crossdomain = false; } SrsHttpApi::~SrsHttpApi() @@ -628,7 +534,8 @@ int SrsHttpApi::do_cycle() SrsAutoFree(SrsHttpMessage, req); // ok, handle http request. - if ((ret = process_request(&skt, req)) != ERROR_SUCCESS) { + SrsGoHttpResponseWriter writer(&skt); + if ((ret = process_request(&writer, req)) != ERROR_SUCCESS) { return ret; } } @@ -636,46 +543,21 @@ int SrsHttpApi::do_cycle() return ret; } -int SrsHttpApi::process_request(SrsStSocket* skt, SrsHttpMessage* req) +int SrsHttpApi::process_request(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) { int ret = ERROR_SUCCESS; - - // parse uri to schema/server:port/path?query - if ((ret = req->parse_uri()) != ERROR_SUCCESS) { - return ret; - } srs_trace("HTTP %s %s, content-length=%"PRId64"", - req->method_str().c_str(), req->url().c_str(), req->content_length()); - - // TODO: maybe need to parse the url. - std::string url = req->path(); - - SrsHttpHandlerMatch* p = NULL; - if ((ret = handler->best_match(url.data(), url.length(), &p)) != ERROR_SUCCESS) { - srs_warn("failed to find the best match handler for url. ret=%d", ret); - return ret; - } + r->method_str().c_str(), r->url().c_str(), r->content_length()); - // if success, p and pstart should be valid. - srs_assert(p); - srs_assert(p->handler); - srs_assert(p->matched_url.length() <= url.length()); - srs_info("best match handler, matched_url=%s", p->matched_url.c_str()); - - req->set_match(p); - req->set_requires_crossdomain(requires_crossdomain); - - // use handler to process request. - if ((ret = p->handler->process_request(skt, req)) != ERROR_SUCCESS) { - srs_warn("handler failed to process http request. ret=%d", ret); + // use default server mux to serve http request. + if ((ret = mux->serve_http(w, r)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("serve http msg failed. ret=%d", ret); + } return ret; } - if (req->requires_crossdomain()) { - requires_crossdomain = true; - } - return ret; } diff --git a/trunk/src/app/srs_app_http_api.hpp b/trunk/src/app/srs_app_http_api.hpp index a29e46066..d21f3683d 100644 --- a/trunk/src/app/srs_app_http_api.hpp +++ b/trunk/src/app/srs_app_http_api.hpp @@ -42,158 +42,130 @@ class SrsHttpHandler; #include // for http root. -class SrsApiRoot : public SrsHttpHandler +class SrsGoApiRoot : public ISrsGoHttpHandler { public: - SrsApiRoot(); - virtual ~SrsApiRoot(); + SrsGoApiRoot(); + virtual ~SrsGoApiRoot(); public: - virtual bool is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase); -protected: - virtual bool can_handle(const char* path, int length, const char** pchild); - virtual int do_process_request(SrsStSocket* skt, SrsHttpMessage* req); + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); }; -class SrsApiApi : public SrsHttpHandler +class SrsGoApiApi : public ISrsGoHttpHandler { public: - SrsApiApi(); - virtual ~SrsApiApi(); + SrsGoApiApi(); + virtual ~SrsGoApiApi(); public: - virtual bool can_handle(const char* path, int length, const char** pchild); -protected: - virtual int do_process_request(SrsStSocket* skt, SrsHttpMessage* req); + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); }; -class SrsApiV1 : public SrsHttpHandler +class SrsGoApiV1 : public ISrsGoHttpHandler { public: - SrsApiV1(); - virtual ~SrsApiV1(); + SrsGoApiV1(); + virtual ~SrsGoApiV1(); public: - virtual bool can_handle(const char* path, int length, const char** pchild); -protected: - virtual int do_process_request(SrsStSocket* skt, SrsHttpMessage* req); + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); }; -class SrsApiRequests : public SrsHttpHandler +class SrsGoApiVersion : public ISrsGoHttpHandler { public: - SrsApiRequests(); - virtual ~SrsApiRequests(); + SrsGoApiVersion(); + virtual ~SrsGoApiVersion(); public: - virtual bool can_handle(const char* path, int length, const char** pchild); -protected: - virtual int do_process_request(SrsStSocket* skt, SrsHttpMessage* req); + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); }; -class SrsApiVersion : public SrsHttpHandler +class SrsGoApiSummaries : public ISrsGoHttpHandler { public: - SrsApiVersion(); - virtual ~SrsApiVersion(); + SrsGoApiSummaries(); + virtual ~SrsGoApiSummaries(); public: - virtual bool can_handle(const char* path, int length, const char** pchild); -protected: - virtual int do_process_request(SrsStSocket* skt, SrsHttpMessage* req); + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); }; -class SrsApiSummaries : public SrsHttpHandler +class SrsGoApiRusages : public ISrsGoHttpHandler { public: - SrsApiSummaries(); - virtual ~SrsApiSummaries(); + SrsGoApiRusages(); + virtual ~SrsGoApiRusages(); public: - virtual bool can_handle(const char* path, int length, const char** pchild); -protected: - virtual int do_process_request(SrsStSocket* skt, SrsHttpMessage* req); + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); }; -class SrsApiRusages : public SrsHttpHandler +class SrsGoApiSelfProcStats : public ISrsGoHttpHandler { public: - SrsApiRusages(); - virtual ~SrsApiRusages(); + SrsGoApiSelfProcStats(); + virtual ~SrsGoApiSelfProcStats(); public: - virtual bool can_handle(const char* path, int length, const char** pchild); -protected: - virtual int do_process_request(SrsStSocket* skt, SrsHttpMessage* req); + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); }; -class SrsApiSelfProcStats : public SrsHttpHandler +class SrsGoApiSystemProcStats : public ISrsGoHttpHandler { public: - SrsApiSelfProcStats(); - virtual ~SrsApiSelfProcStats(); + SrsGoApiSystemProcStats(); + virtual ~SrsGoApiSystemProcStats(); public: - virtual bool can_handle(const char* path, int length, const char** pchild); -protected: - virtual int do_process_request(SrsStSocket* skt, SrsHttpMessage* req); + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); }; -class SrsApiSystemProcStats : public SrsHttpHandler +class SrsGoApiMemInfos : public ISrsGoHttpHandler { public: - SrsApiSystemProcStats(); - virtual ~SrsApiSystemProcStats(); + SrsGoApiMemInfos(); + virtual ~SrsGoApiMemInfos(); public: - virtual bool can_handle(const char* path, int length, const char** pchild); -protected: - virtual int do_process_request(SrsStSocket* skt, SrsHttpMessage* req); + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); }; -class SrsApiMemInfos : public SrsHttpHandler +class SrsGoApiAuthors : public ISrsGoHttpHandler { public: - SrsApiMemInfos(); - virtual ~SrsApiMemInfos(); + SrsGoApiAuthors(); + virtual ~SrsGoApiAuthors(); public: - virtual bool can_handle(const char* path, int length, const char** pchild); -protected: - virtual int do_process_request(SrsStSocket* skt, SrsHttpMessage* req); + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); }; -class SrsApiAuthors : public SrsHttpHandler +class SrsGoApiRequests : public ISrsGoHttpHandler { public: - SrsApiAuthors(); - virtual ~SrsApiAuthors(); + SrsGoApiRequests(); + virtual ~SrsGoApiRequests(); public: - virtual bool can_handle(const char* path, int length, const char** pchild); -protected: - virtual int do_process_request(SrsStSocket* skt, SrsHttpMessage* req); + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); }; -class SrsApiVhosts : public SrsHttpHandler +class SrsGoApiVhosts : public ISrsGoHttpHandler { public: - SrsApiVhosts(); - virtual ~SrsApiVhosts(); + SrsGoApiVhosts(); + virtual ~SrsGoApiVhosts(); public: - virtual bool can_handle(const char* path, int length, const char** pchild); -protected: - virtual int do_process_request(SrsStSocket* skt, SrsHttpMessage* req); + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); }; -class SrsApiStreams : public SrsHttpHandler +class SrsGoApiStreams : public ISrsGoHttpHandler { public: - SrsApiStreams(); - virtual ~SrsApiStreams(); + SrsGoApiStreams(); + virtual ~SrsGoApiStreams(); public: - virtual bool can_handle(const char* path, int length, const char** pchild); -protected: - virtual int do_process_request(SrsStSocket* skt, SrsHttpMessage* req); + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); }; class SrsHttpApi : public SrsConnection { private: SrsHttpParser* parser; - SrsHttpHandler* handler; - bool requires_crossdomain; + SrsGoHttpServeMux* mux; public: - SrsHttpApi(SrsServer* srs_server, st_netfd_t client_stfd, SrsHttpHandler* _handler); + SrsHttpApi(SrsServer* svr, st_netfd_t fd, SrsGoHttpServeMux* m); virtual ~SrsHttpApi(); public: virtual void kbps_resample(); @@ -204,7 +176,7 @@ public: protected: virtual int do_cycle(); private: - virtual int process_request(SrsStSocket* skt, SrsHttpMessage* req); + virtual int process_request(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); }; #endif diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp index 744307e29..a3ccef6e7 100644 --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -549,11 +549,6 @@ int SrsHttpConn::do_cycle() int SrsHttpConn::process_request(SrsStSocket* skt, SrsHttpMessage* req) { int ret = ERROR_SUCCESS; - - // parse uri to schema/server:port/path?query - if ((ret = req->parse_uri()) != ERROR_SUCCESS) { - return ret; - } srs_trace("HTTP %s %s, content-length=%"PRId64"", req->method_str().c_str(), req->url().c_str(), req->content_length()); diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index b014bbdea..ca96062bc 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -330,7 +330,7 @@ SrsServer::SrsServer() // for some global instance is not ready now, // new these objects in initialize instead. #ifdef SRS_AUTO_HTTP_API - http_api_handler = NULL; + http_api_mux = new SrsGoHttpServeMux(); #endif #ifdef SRS_AUTO_HTTP_SERVER http_stream_handler = NULL; @@ -363,7 +363,7 @@ void SrsServer::destroy() #endif #ifdef SRS_AUTO_HTTP_API - srs_freep(http_api_handler); + srs_freep(http_api_mux); #endif #ifdef SRS_AUTO_HTTP_SERVER @@ -415,8 +415,52 @@ int SrsServer::initialize() kbps->set_io(NULL, NULL); #ifdef SRS_AUTO_HTTP_API - srs_assert(!http_api_handler); - http_api_handler = SrsHttpHandler::create_http_api(); + if ((ret = http_api_mux->initialize()) != ERROR_SUCCESS) { + return ret; + } +#endif + +#ifdef SRS_AUTO_HTTP_API + srs_assert(http_api_mux); + if ((ret = http_api_mux->handle("/", new SrsGoApiRoot())) != ERROR_SUCCESS) { + return ret; + } + if ((ret = http_api_mux->handle("/api", new SrsGoApiApi())) != ERROR_SUCCESS) { + return ret; + } + if ((ret = http_api_mux->handle("/api/v1", new SrsGoApiV1())) != ERROR_SUCCESS) { + return ret; + } + if ((ret = http_api_mux->handle("/api/v1/versions", new SrsGoApiVersion())) != ERROR_SUCCESS) { + return ret; + } + if ((ret = http_api_mux->handle("/api/v1/summaries", new SrsGoApiSummaries())) != ERROR_SUCCESS) { + return ret; + } + if ((ret = http_api_mux->handle("/api/v1/rusages", new SrsGoApiRusages())) != ERROR_SUCCESS) { + return ret; + } + if ((ret = http_api_mux->handle("/api/v1/self_proc_stats", new SrsGoApiSelfProcStats())) != ERROR_SUCCESS) { + return ret; + } + if ((ret = http_api_mux->handle("/api/v1/system_proc_stats", new SrsGoApiSystemProcStats())) != ERROR_SUCCESS) { + return ret; + } + if ((ret = http_api_mux->handle("/api/v1/meminfos", new SrsGoApiMemInfos())) != ERROR_SUCCESS) { + return ret; + } + if ((ret = http_api_mux->handle("/api/v1/authors", new SrsGoApiAuthors())) != ERROR_SUCCESS) { + return ret; + } + if ((ret = http_api_mux->handle("/api/v1/requests", new SrsGoApiRequests())) != ERROR_SUCCESS) { + return ret; + } + if ((ret = http_api_mux->handle("/api/v1/vhosts", new SrsGoApiVhosts())) != ERROR_SUCCESS) { + return ret; + } + if ((ret = http_api_mux->handle("/api/v1/streams", new SrsGoApiStreams())) != ERROR_SUCCESS) { + return ret; + } #endif #ifdef SRS_AUTO_HTTP_SERVER srs_assert(!http_stream_handler); @@ -430,12 +474,6 @@ int SrsServer::initialize() srs_assert(!ingester); ingester = new SrsIngester(); #endif - -#ifdef SRS_AUTO_HTTP_API - if ((ret = http_api_handler->initialize()) != ERROR_SUCCESS) { - return ret; - } -#endif #ifdef SRS_AUTO_HTTP_SERVER if ((ret = http_stream_handler->initialize()) != ERROR_SUCCESS) { @@ -896,7 +934,7 @@ int SrsServer::accept_client(SrsListenerType type, st_netfd_t client_stfd) conn = new SrsRtmpConn(this, client_stfd); } else if (type == SrsListenerHttpApi) { #ifdef SRS_AUTO_HTTP_API - conn = new SrsHttpApi(this, client_stfd, http_api_handler); + conn = new SrsHttpApi(this, client_stfd, http_api_mux); #else srs_warn("close http client for server not support http-api"); srs_close_stfd(client_stfd); diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index ef1817a72..69e57885d 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -38,6 +38,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class SrsServer; class SrsConnection; +class SrsGoHttpServeMux; class SrsHttpHandler; class SrsIngester; class SrsHttpHeartbeat; @@ -116,7 +117,7 @@ class SrsServer : public ISrsReloadHandler { private: #ifdef SRS_AUTO_HTTP_API - SrsHttpHandler* http_api_handler; + SrsGoHttpServeMux* http_api_mux; #endif #ifdef SRS_AUTO_HTTP_SERVER SrsHttpHandler* http_stream_handler; diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 34573ab11..945f2232d 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 95 +#define VERSION_REVISION 96 // server info. #define RTMP_SIG_SRS_KEY "SRS" diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index d8907ef96..f4b2640e6 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -204,6 +204,14 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_AAC_DATA_INVALID 3048 #define ERROR_HTTP_STATUS_INVLIAD 3049 +/////////////////////////////////////////////////////// +// HTTP protocol error. +/////////////////////////////////////////////////////// +#define ERROR_HTTP_PATTERN_EMPTY 4000 +#define ERROR_HTTP_PATTERN_DUPLICATED 4001 +#define ERROR_HTTP_URL_NOT_CLEAN 4002 +#define ERROR_HTTP_CONTENT_LENGTH 4003 + /** * whether the error code is an system control error. */