diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 15e976e88..7faccbb75 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -85,13 +85,6 @@ http_stream { # for example, user use ip to access the stream: rtmp://192.168.1.2/live/livestream. # for which cannot identify the required vhost. vhost __defaultVhost__ { - enabled on; - gop_cache on; - http { - enabled on; - mount /; - dir ./objs/nginx/html; - } } # vhost for http @@ -103,9 +96,11 @@ vhost http.srs.com { enabled on; # the virtual directory root for this vhost to mount at # for example, if mount to /hls, user access by http://server/hls + # default: / mount /hls; # main dir of vhost, # to delivery HTTP stream of this vhost. + # default: ./objs/nginx/html dir ./objs/nginx/html; } } diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 9c3426b7a..e1970df58 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -286,7 +286,7 @@ int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type) } // see: ngx_conf_read_token -int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector& args) +int SrsConfDirective::read_token(SrsFileBuffer* buffer, vector& args) { int ret = ERROR_SUCCESS; @@ -427,6 +427,11 @@ int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector& arg return ret; } +bool SrsConfDirective::is_vhost() +{ + return name == "vhost"; +} + SrsConfig::SrsConfig() { show_help = false; @@ -777,6 +782,11 @@ bool SrsConfig::get_deamon() return true; } +SrsConfDirective* SrsConfig::get_root() +{ + return root; +} + int SrsConfig::get_max_connections() { srs_assert(root); @@ -835,6 +845,21 @@ int SrsConfig::get_pithy_print_forwarder() return ::atoi(pithy->arg0().c_str()); } +int SrsConfig::get_pithy_print_encoder() +{ + SrsConfDirective* pithy = root->get("encoder"); + if (!pithy) { + return SRS_STAGE_ENCODER_INTERVAL_MS; + } + + pithy = pithy->get("forwarder"); + if (!pithy) { + return SRS_STAGE_ENCODER_INTERVAL_MS; + } + + return ::atoi(pithy->arg0().c_str()); +} + int SrsConfig::get_pithy_print_hls() { SrsConfDirective* pithy = root->get("pithy_print"); @@ -850,6 +875,21 @@ int SrsConfig::get_pithy_print_hls() return ::atoi(pithy->arg0().c_str()); } +int SrsConfig::get_pithy_print_play() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return SRS_STAGE_PLAY_USER_INTERVAL_MS; + } + + pithy = pithy->get("play"); + if (!pithy) { + return SRS_STAGE_PLAY_USER_INTERVAL_MS; + } + + return ::atoi(pithy->arg0().c_str()); +} + SrsConfDirective* SrsConfig::get_vhost(string vhost) { srs_assert(root); @@ -857,7 +897,7 @@ SrsConfDirective* SrsConfig::get_vhost(string vhost) for (int i = 0; i < (int)root->directives.size(); i++) { SrsConfDirective* conf = root->at(i); - if (conf->name != "vhost") { + if (!conf->is_vhost()) { continue; } @@ -1116,7 +1156,7 @@ SrsConfDirective* SrsConfig::get_refer_publish(string vhost) return conf->get("refer_publish"); } -int SrsConfig::get_chunk_size(const std::string &vhost) +int SrsConfig::get_chunk_size(const string &vhost) { SrsConfDirective* conf = get_vhost(vhost); @@ -1271,7 +1311,7 @@ string SrsConfig::get_transcode_ffmpeg(SrsConfDirective* transcode) return conf->arg0(); } -void SrsConfig::get_transcode_engines(SrsConfDirective* transcode, std::vector& engines) +void SrsConfig::get_transcode_engines(SrsConfDirective* transcode, vector& engines) { if (!transcode) { return; @@ -1414,7 +1454,7 @@ string SrsConfig::get_engine_vpreset(SrsConfDirective* engine) return conf->arg0(); } -void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector& vparams) +void SrsConfig::get_engine_vparams(SrsConfDirective* engine, vector& vparams) { if (!engine) { return; @@ -1436,7 +1476,7 @@ void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector } } -void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector& vfilter) +void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, vector& vfilter) { if (!engine) { return; @@ -1514,7 +1554,7 @@ int SrsConfig::get_engine_achannels(SrsConfDirective* engine) return ::atoi(conf->arg0().c_str()); } -void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector& aparams) +void SrsConfig::get_engine_aparams(SrsConfDirective* engine, vector& aparams) { if (!engine) { return; @@ -1754,34 +1794,68 @@ int SrsConfig::get_http_stream_listen() return 8080; } -int SrsConfig::get_pithy_print_encoder() +bool SrsConfig::get_vhost_http_enabled(string vhost) { - SrsConfDirective* pithy = root->get("encoder"); - if (!pithy) { - return SRS_STAGE_ENCODER_INTERVAL_MS; + SrsConfDirective* conf = get_vhost(vhost); + if (!conf) { + return false; } - pithy = pithy->get("forwarder"); - if (!pithy) { - return SRS_STAGE_ENCODER_INTERVAL_MS; + conf = conf->get("http"); + if (!conf) { + return false; } - return ::atoi(pithy->arg0().c_str()); + conf = conf->get("enabled"); + if (!conf) { + return false; + } + + if (conf->arg0() == "on") { + return true; + } + + return false; } -int SrsConfig::get_pithy_print_play() +string SrsConfig::get_vhost_http_mount(string vhost) { - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return SRS_STAGE_PLAY_USER_INTERVAL_MS; + SrsConfDirective* conf = get_vhost(vhost); + if (!conf) { + return SRS_CONF_DEFAULT_HTTP_MOUNT; } - pithy = pithy->get("play"); - if (!pithy) { - return SRS_STAGE_PLAY_USER_INTERVAL_MS; + conf = conf->get("http"); + if (!conf) { + return SRS_CONF_DEFAULT_HTTP_MOUNT; } - return ::atoi(pithy->arg0().c_str()); + conf = conf->get("mount"); + if (!conf || conf->arg0().empty()) { + return SRS_CONF_DEFAULT_HTTP_MOUNT; + } + + return conf->arg0(); +} + +string SrsConfig::get_vhost_http_dir(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + if (!conf) { + return SRS_CONF_DEFAULT_HTTP_DIR; + } + + conf = conf->get("http"); + if (!conf) { + return SRS_CONF_DEFAULT_HTTP_DIR; + } + + conf = conf->get("dir"); + if (!conf || conf->arg0().empty()) { + return SRS_CONF_DEFAULT_HTTP_DIR; + } + + return conf->arg0(); } bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b) diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index b42cda4d7..5ea597472 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -56,6 +56,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // the interval in seconds for bandwidth check #define SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS 1000 +#define SRS_CONF_DEFAULT_HTTP_MOUNT "/" +#define SRS_CONF_DEFAULT_HTTP_DIR SRS_CONF_DEFAULT_HLS_PATH + #define SRS_STAGE_PLAY_USER_INTERVAL_MS 1300 #define SRS_STAGE_PUBLISH_USER_INTERVAL_MS 1100 #define SRS_STAGE_FORWARDER_INTERVAL_MS 2000 @@ -86,6 +89,8 @@ public: enum SrsDirectiveType{parse_file, parse_block}; virtual int parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type); virtual int read_token(SrsFileBuffer* buffer, std::vector& args); +public: + virtual bool is_vhost(); }; /** @@ -118,6 +123,7 @@ private: virtual void print_help(char** argv); // global section public: + virtual SrsConfDirective* get_root(); virtual bool get_deamon(); virtual int get_max_connections(); virtual SrsConfDirective* get_listen(); @@ -201,6 +207,10 @@ private: public: virtual bool get_http_stream_enabled(); virtual int get_http_stream_listen(); +public: + virtual bool get_vhost_http_enabled(std::string vhost); + virtual std::string get_vhost_http_mount(std::string vhost); + virtual std::string get_vhost_http_dir(std::string vhost); }; /** diff --git a/trunk/src/app/srs_app_http.cpp b/trunk/src/app/srs_app_http.cpp index df8a55edb..e40249121 100644 --- a/trunk/src/app/srs_app_http.cpp +++ b/trunk/src/app/srs_app_http.cpp @@ -325,8 +325,7 @@ SrsHttpHandler* SrsHttpHandler::create_http_api() SrsHttpHandler* SrsHttpHandler::create_http_stream() { - // TODO: FIXME: use http stream handler instead. - return new SrsHttpHandler(); + return new SrsHttpRoot(); } SrsHttpMessage::SrsHttpMessage() diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp index 0d8ae6aa8..7002a1f35 100644 --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -32,16 +32,151 @@ using namespace std; #include #include #include -#include #include +#include +#include -#define SRS_HTTP_HEADER_BUFFER 1024 +SrsHttpRoot::SrsHttpRoot() +{ + // TODO: FIXME: support reload vhosts. +} + +SrsHttpRoot::~SrsHttpRoot() +{ +} + +int SrsHttpRoot::initialize() +{ + int ret = ERROR_SUCCESS; + + SrsConfDirective* root = _srs_config->get_root(); + for (int i = 0; i < (int)root->directives.size(); i++) { + SrsConfDirective* conf = root->at(i); + + if (!conf->is_vhost()) { + continue; + } + + std::string vhost = conf->arg0(); + if (!_srs_config->get_vhost_http_enabled(vhost)) { + continue; + } + + std::string mount = _srs_config->get_vhost_http_mount(vhost); + std::string dir = _srs_config->get_vhost_http_dir(vhost); + + handlers.push_back(new SrsHttpVhost(vhost, mount, dir)); + } + + return ret; +} + +bool SrsHttpRoot::is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase) +{ + if (!SrsHttpHandler::is_handler_valid(req, status_code, reason_phrase)) { + return false; + } + + if (req->match()->matched_url.length() != 1) { + status_code = HTTP_NotFound; + reason_phrase = HTTP_NotFound_str; + return false; + } + + return true; +} + +bool SrsHttpRoot::can_handle(const char* path, int length, const char** pchild) +{ + // reset the child path to path, + // for child to reparse the path. + *pchild = path; + + // only compare the first char. + return srs_path_equals("/", path, 1); +} + +int SrsHttpRoot::do_process_request(SrsSocket* skt, SrsHttpMessage* req) +{ + std::stringstream ss; + + ss << JOBJECT_START + << JFIELD_ERROR(ERROR_SUCCESS) << JFIELD_CONT + << JFIELD_ORG("urls", JOBJECT_START); + + vector::iterator it; + for (it = handlers.begin(); it != handlers.end(); ++it) { + SrsHttpVhost* handler = dynamic_cast(*it); + srs_assert(handler); + + ss << JFIELD_ORG(handler->mount(), JOBJECT_START) + << JFIELD_STR("mount", handler->mount()) << JFIELD_CONT + << JFIELD_STR("vhost", handler->vhost()) << JFIELD_CONT + << JFIELD_STR("dir", handler->dir()) + << JOBJECT_END; + + if (it + 1 != handlers.end()) { + ss << JFIELD_CONT; + } + } + + ss << JOBJECT_END + << JOBJECT_END; + + return res_json(skt, req, ss.str()); +} + +SrsHttpVhost::SrsHttpVhost(std::string vhost, std::string mount, std::string dir) +{ + _vhost = vhost; + _mount = mount; + _dir = dir; +} + +SrsHttpVhost::~SrsHttpVhost() +{ +} + +bool SrsHttpVhost::can_handle(const char* path, int length, const char** /*pchild*/) +{ + return srs_path_equals("/api", path, length); +} + +int SrsHttpVhost::do_process_request(SrsSocket* skt, SrsHttpMessage* req) +{ + std::stringstream ss; + + ss << JOBJECT_START + << JFIELD_ERROR(ERROR_SUCCESS) << JFIELD_CONT + << JFIELD_ORG("urls", JOBJECT_START) + << JFIELD_STR("v1", "the api version 1.0") + << JOBJECT_END + << JOBJECT_END; + + return res_json(skt, req, ss.str()); +} + +string SrsHttpVhost::vhost() +{ + return _vhost; +} + +string SrsHttpVhost::mount() +{ + return _mount; +} + +string SrsHttpVhost::dir() +{ + return _dir; +} SrsHttpConn::SrsHttpConn(SrsServer* srs_server, st_netfd_t client_stfd, SrsHttpHandler* _handler) : SrsConnection(srs_server, client_stfd) { parser = new SrsHttpParser(); handler = _handler; + requires_crossdomain = false; } SrsHttpConn::~SrsHttpConn() @@ -96,34 +231,41 @@ int SrsHttpConn::do_cycle() int SrsHttpConn::process_request(SrsSocket* skt, SrsHttpMessage* req) { int ret = ERROR_SUCCESS; + + // parse uri to schema/server:port/path?query + if ((ret = req->parse_uri()) != ERROR_SUCCESS) { + return ret; + } - if (req->method() == HTTP_OPTIONS) { - char data[] = "HTTP/1.1 200 OK" __CRLF - "Content-Length: 0"__CRLF - "Server: SRS/"RTMP_SIG_SRS_VERSION""__CRLF - "Allow: DELETE, GET, HEAD, OPTIONS, POST, PUT"__CRLF - "Access-Control-Allow-Origin: *"__CRLF - "Access-Control-Allow-Methods: GET, POST, HEAD, PUT, DELETE"__CRLF - "Access-Control-Allow-Headers: Cache-Control,X-Proxy-Authorization,X-Requested-With,Content-Type"__CRLF - "Content-Type: text/html;charset=utf-8"__CRLFCRLF - ""; - return skt->write(data, sizeof(data), NULL); - } else { - std::string tilte = "SRS/"RTMP_SIG_SRS_VERSION; - tilte += " hello http/1.1 server~\n"; - - std::stringstream ss; - ss << "HTTP/1.1 200 OK " << __CRLF - << "Content-Length: "<< tilte.length() + req->body_size() << __CRLF - << "Server: SRS/"RTMP_SIG_SRS_VERSION"" << __CRLF - << "Allow: DELETE, GET, HEAD, OPTIONS, POST, PUT" << __CRLF - << "Access-Control-Allow-Origin: *" << __CRLF - << "Access-Control-Allow-Methods: GET, POST, HEAD, PUT, DELETE" << __CRLF - << "Access-Control-Allow-Headers: Cache-Control,X-Proxy-Authorization,X-Requested-With,Content-Type" << __CRLF - << "Content-Type: text/html;charset=utf-8" << __CRLFCRLF - << tilte << req->body().c_str() - << ""; - return skt->write(ss.str().c_str(), ss.str().length(), NULL); + srs_trace("http request parsed, method=%d, url=%s, content-length=%"PRId64"", + req->method(), req->url().c_str(), req->content_length()); + + // TODO: maybe need to parse the url. + std::string url = req->path(); + + SrsHttpHandlerMatch* p = NULL; + if ((ret = handler->best_match(url.data(), url.length(), &p)) != ERROR_SUCCESS) { + srs_warn("failed to find the best match handler for url. ret=%d", ret); + return ret; + } + + // if success, p and pstart should be valid. + srs_assert(p); + srs_assert(p->handler); + srs_assert(p->matched_url.length() <= url.length()); + srs_info("best match handler, matched_url=%s", p->matched_url.c_str()); + + req->set_match(p); + req->set_requires_crossdomain(requires_crossdomain); + + // use handler to process request. + if ((ret = p->handler->process_request(skt, req)) != ERROR_SUCCESS) { + srs_warn("handler failed to process http request. ret=%d", ret); + return ret; + } + + if (req->requires_crossdomain()) { + requires_crossdomain = true; } return ret; diff --git a/trunk/src/app/srs_app_http_conn.hpp b/trunk/src/app/srs_app_http_conn.hpp index fc26d905e..bb0370c96 100644 --- a/trunk/src/app/srs_app_http_conn.hpp +++ b/trunk/src/app/srs_app_http_conn.hpp @@ -34,19 +34,50 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include - -#include +#include class SrsSocket; class SrsHttpParser; class SrsHttpMessage; class SrsHttpHandler; +// for http root. +class SrsHttpRoot : public SrsHttpHandler +{ +public: + SrsHttpRoot(); + virtual ~SrsHttpRoot(); +public: + virtual int initialize(); + virtual bool is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase); + virtual bool can_handle(const char* path, int length, const char** pchild); + virtual int do_process_request(SrsSocket* skt, SrsHttpMessage* req); +}; + +class SrsHttpVhost : public SrsHttpHandler +{ +private: + std::string _vhost; + std::string _mount; + std::string _dir; +public: + SrsHttpVhost(std::string vhost, std::string mount, std::string dir); + virtual ~SrsHttpVhost(); +public: + virtual bool can_handle(const char* path, int length, const char** pchild); + virtual int do_process_request(SrsSocket* skt, SrsHttpMessage* req); +public: + virtual std::string vhost(); + virtual std::string mount(); + virtual std::string dir(); +}; + class SrsHttpConn : public SrsConnection { private: SrsHttpParser* parser; SrsHttpHandler* handler; + bool requires_crossdomain; public: SrsHttpConn(SrsServer* srs_server, st_netfd_t client_stfd, SrsHttpHandler* _handler); virtual ~SrsHttpConn(); diff --git a/trunk/src/app/srs_app_json.hpp b/trunk/src/app/srs_app_json.hpp index ef15847fa..ea64cfb6b 100644 --- a/trunk/src/app/srs_app_json.hpp +++ b/trunk/src/app/srs_app_json.hpp @@ -36,5 +36,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define JFIELD_ERROR(ret) "\"" << "code" << "\":" << ret #define JFIELD_CONT "," #define JOBJECT_END "}" +#define JARRAY_START "[" +#define JARRAY_END "]" #endif \ No newline at end of file