diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 7faccbb75..297c0d97b 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -76,6 +76,9 @@ http_stream { # @remark, if use lower port, for instance 80, user must start srs by root. # default: 8080 listen 8080; + # the default dir for http root. + # 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 e1970df58..143bd3d5a 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -1746,15 +1746,16 @@ int SrsConfig::get_http_api_listen() { SrsConfDirective* conf = get_http_api(); - if (conf) { - conf = conf->get("listen"); - - if (conf && !conf->arg0().empty()) { - return ::atoi(conf->arg0().c_str()); - } + if (!conf) { + return SRS_CONF_DEFAULT_HTTP_API_PORT; } - return 1985; + conf = conf->get("listen"); + if (!conf || conf->arg0().empty()) { + return SRS_CONF_DEFAULT_HTTP_API_PORT; + } + + return ::atoi(conf->arg0().c_str()); } SrsConfDirective* SrsConfig::get_http_stream() @@ -1765,13 +1766,11 @@ SrsConfDirective* SrsConfig::get_http_stream() bool SrsConfig::get_http_stream_enabled() { SrsConfDirective* conf = get_http_stream(); - if (!conf) { return false; } conf = conf->get("enabled"); - if (conf && conf->arg0() == "on") { return true; } @@ -1783,15 +1782,35 @@ int SrsConfig::get_http_stream_listen() { SrsConfDirective* conf = get_http_stream(); - if (conf) { - conf = conf->get("listen"); - - if (conf && !conf->arg0().empty()) { - return ::atoi(conf->arg0().c_str()); - } + if (!conf) { + return SRS_CONF_DEFAULT_HTTP_STREAM_PORT; } - return 8080; + conf = conf->get("listen"); + if (!conf || conf->arg0().empty()) { + return SRS_CONF_DEFAULT_HTTP_STREAM_PORT; + } + + return ::atoi(conf->arg0().c_str()); +} + +string SrsConfig::get_http_stream_dir() +{ + SrsConfDirective* conf = get_http_stream(); + if (!conf) { + return SRS_CONF_DEFAULT_HTTP_DIR; + } + + conf = conf->get("dir"); + if (!conf) { + return SRS_CONF_DEFAULT_HTTP_DIR; + } + + if (conf->arg0().empty()) { + return SRS_CONF_DEFAULT_HTTP_DIR; + } + + return conf->arg0(); } bool SrsConfig::get_vhost_http_enabled(string vhost) diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 5ea597472..43d2f4162 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -59,6 +59,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define SRS_CONF_DEFAULT_HTTP_MOUNT "/" #define SRS_CONF_DEFAULT_HTTP_DIR SRS_CONF_DEFAULT_HLS_PATH +#define SRS_CONF_DEFAULT_HTTP_STREAM_PORT 8080 +#define SRS_CONF_DEFAULT_HTTP_API_PORT 1985 + #define SRS_STAGE_PLAY_USER_INTERVAL_MS 1300 #define SRS_STAGE_PUBLISH_USER_INTERVAL_MS 1100 #define SRS_STAGE_FORWARDER_INTERVAL_MS 2000 @@ -207,6 +210,7 @@ private: public: virtual bool get_http_stream_enabled(); virtual int get_http_stream_listen(); + virtual std::string get_http_stream_dir(); public: virtual bool get_vhost_http_enabled(std::string vhost); virtual std::string get_vhost_http_mount(std::string vhost); diff --git a/trunk/src/app/srs_app_http.cpp b/trunk/src/app/srs_app_http.cpp index e40249121..513b54776 100644 --- a/trunk/src/app/srs_app_http.cpp +++ b/trunk/src/app/srs_app_http.cpp @@ -48,7 +48,8 @@ bool srs_path_equals(const char* expect, const char* path, int nb_path) return false; } - return !memcmp(expect, path, size); + bool equals = !memcmp(expect, path, size); + return equals; } SrsHttpHandlerMatch::SrsHttpHandlerMatch() @@ -223,6 +224,20 @@ SrsHttpHandler* SrsHttpHandler::res_content_type_json(std::stringstream& ss) return this; } +SrsHttpHandler* SrsHttpHandler::res_content_type_m3u8(std::stringstream& ss) +{ + ss << "Content-Type: application/x-mpegURL;charset=utf-8" << __CRLF + << "Allow: DELETE, GET, HEAD, OPTIONS, POST, PUT" << __CRLF; + return this; +} + +SrsHttpHandler* SrsHttpHandler::res_content_type_mpegts(std::stringstream& ss) +{ + ss << "Content-Type: video/MP2T;charset=utf-8" << __CRLF + << "Allow: DELETE, GET, HEAD, OPTIONS, POST, PUT" << __CRLF; + return this; +} + SrsHttpHandler* SrsHttpHandler::res_content_length(std::stringstream& ss, int64_t length) { ss << "Content-Length: "<< length << __CRLF; @@ -284,6 +299,40 @@ int SrsHttpHandler::res_text(SrsSocket* skt, SrsHttpMessage* req, std::string bo return res_flush(skt, ss); } +int SrsHttpHandler::res_m3u8(SrsSocket* skt, SrsHttpMessage* req, std::string body) +{ + std::stringstream ss; + + res_status_line(ss)->res_content_type_m3u8(ss) + ->res_content_length(ss, (int)body.length()); + + if (req->requires_crossdomain()) { + res_enable_crossdomain(ss); + } + + res_header_eof(ss) + ->res_body(ss, body); + + return res_flush(skt, ss); +} + +int SrsHttpHandler::res_mpegts(SrsSocket* skt, SrsHttpMessage* req, std::string body) +{ + std::stringstream ss; + + res_status_line(ss)->res_content_type_mpegts(ss) + ->res_content_length(ss, (int)body.length()); + + if (req->requires_crossdomain()) { + res_enable_crossdomain(ss); + } + + res_header_eof(ss) + ->res_body(ss, body); + + return res_flush(skt, ss); +} + int SrsHttpHandler::res_json(SrsSocket* skt, SrsHttpMessage* req, std::string json) { std::stringstream ss; diff --git a/trunk/src/app/srs_app_http.hpp b/trunk/src/app/srs_app_http.hpp index 621e7cd71..af0cae24c 100644 --- a/trunk/src/app/srs_app_http.hpp +++ b/trunk/src/app/srs_app_http.hpp @@ -229,6 +229,8 @@ public: virtual SrsHttpHandler* res_status_line_error(std::stringstream& ss, int code, std::string reason_phrase); virtual SrsHttpHandler* res_content_type(std::stringstream& ss); virtual SrsHttpHandler* res_content_type_json(std::stringstream& ss); + virtual SrsHttpHandler* res_content_type_m3u8(std::stringstream& ss); + virtual SrsHttpHandler* res_content_type_mpegts(std::stringstream& ss); virtual SrsHttpHandler* res_content_length(std::stringstream& ss, int64_t length); virtual SrsHttpHandler* res_enable_crossdomain(std::stringstream& ss); virtual SrsHttpHandler* res_header_eof(std::stringstream& ss); @@ -237,6 +239,8 @@ public: public: virtual int res_options(SrsSocket* skt); virtual int res_text(SrsSocket* skt, SrsHttpMessage* req, std::string body); + virtual int res_m3u8(SrsSocket* skt, SrsHttpMessage* req, std::string body); + virtual int res_mpegts(SrsSocket* skt, SrsHttpMessage* req, std::string body); virtual int res_json(SrsSocket* skt, SrsHttpMessage* req, std::string json); virtual int res_error(SrsSocket* skt, SrsHttpMessage* req, int code, std::string reason_phrase, std::string body); // object creator diff --git a/trunk/src/app/srs_app_http_api.hpp b/trunk/src/app/srs_app_http_api.hpp index 23a5f86d6..62fee9f76 100644 --- a/trunk/src/app/srs_app_http_api.hpp +++ b/trunk/src/app/srs_app_http_api.hpp @@ -49,6 +49,7 @@ public: virtual ~SrsApiRoot(); 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(SrsSocket* skt, SrsHttpMessage* req); }; @@ -60,6 +61,7 @@ public: virtual ~SrsApiApi(); public: virtual bool can_handle(const char* path, int length, const char** pchild); +protected: virtual int do_process_request(SrsSocket* skt, SrsHttpMessage* req); }; @@ -70,6 +72,7 @@ public: virtual ~SrsApiV1(); public: virtual bool can_handle(const char* path, int length, const char** pchild); +protected: virtual int do_process_request(SrsSocket* skt, SrsHttpMessage* req); }; @@ -80,6 +83,7 @@ public: virtual ~SrsApiVersion(); public: virtual bool can_handle(const char* path, int length, const char** pchild); +protected: virtual int do_process_request(SrsSocket* skt, SrsHttpMessage* req); }; @@ -90,6 +94,7 @@ public: virtual ~SrsApiAuthors(); public: virtual bool can_handle(const char* path, int length, const char** pchild); +protected: virtual int do_process_request(SrsSocket* skt, SrsHttpMessage* req); }; diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp index 7002a1f35..58ee546cd 100644 --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -28,6 +28,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include using namespace std; +#include +#include +#include + #include #include #include @@ -48,7 +52,11 @@ SrsHttpRoot::~SrsHttpRoot() int SrsHttpRoot::initialize() { int ret = ERROR_SUCCESS; + + // add root + handlers.push_back(new SrsHttpVhost("__http__", "/", _srs_config->get_http_stream_dir())); + // add other virtual path SrsConfDirective* root = _srs_config->get_root(); for (int i = 0; i < (int)root->directives.size(); i++) { SrsConfDirective* conf = root->at(i); @@ -71,6 +79,16 @@ int SrsHttpRoot::initialize() return ret; } +bool SrsHttpRoot::can_handle(const char* path, int length, const char** pchild) +{ + // reset the child path to path, + // for child to reparse the path. + *pchild = path; + + // only compare the first char. + return srs_path_equals("/", path, 1); +} + bool SrsHttpRoot::is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase) { if (!SrsHttpHandler::is_handler_valid(req, status_code, reason_phrase)) { @@ -86,16 +104,6 @@ bool SrsHttpRoot::is_handler_valid(SrsHttpMessage* req, int& status_code, std::s 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; @@ -139,21 +147,74 @@ SrsHttpVhost::~SrsHttpVhost() bool SrsHttpVhost::can_handle(const char* path, int length, const char** /*pchild*/) { - return srs_path_equals("/api", path, length); + int min_match = srs_min(length, (int)_mount.length()); + return srs_path_equals(_mount.c_str(), path, min_match); +} + +bool SrsHttpVhost::is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase) +{ + std::string fullpath = _dir + "/" + req->match()->unmatched_url; + if (req->match()->unmatched_url.empty()) { + fullpath += req->match()->matched_url; + } + + if (::access(fullpath.c_str(), F_OK | R_OK) < 0) { + srs_warn("check file %s does not exists", fullpath.c_str()); + + status_code = HTTP_NotFound; + reason_phrase = HTTP_NotFound_str; + return false; + } + + return true; } int SrsHttpVhost::do_process_request(SrsSocket* skt, SrsHttpMessage* req) { - std::stringstream ss; + int ret = ERROR_SUCCESS; - 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; + std::string fullpath = _dir + "/" + req->match()->unmatched_url; + if (req->match()->unmatched_url.empty()) { + fullpath += req->match()->matched_url; + } - return res_json(skt, req, ss.str()); + if (srs_string_ends_with(fullpath, "/")) { + fullpath += "index.html"; + } + + int fd = ::open(fullpath.c_str(), O_RDONLY); + if (fd < 0) { + ret = ERROR_HTTP_OPEN_FILE; + srs_warn("open file %s failed, ret=%d", fullpath.c_str(), ret); + return ret; + } + + int64_t length = (int64_t)::lseek(fd, 0, SEEK_END); + ::lseek(fd, 0, SEEK_SET); + + char* buf = new char[length]; + SrsAutoFree(char, buf, true); + + if (::read(fd, buf, length) < 0) { + ::close(fd); + ret = ERROR_HTTP_READ_FILE; + srs_warn("read file %s failed, ret=%d", fullpath.c_str(), ret); + return ret; + } + ::close(fd); + + std::string str; + str.append(buf, length); + + if (srs_string_ends_with(fullpath, ".ts")) { + return res_mpegts(skt, req, str); + } else if (srs_string_ends_with(fullpath, ".m3u8")) { + return res_m3u8(skt, req, str); + } else { + return res_text(skt, req, str); + } + + return ret; } string SrsHttpVhost::vhost() diff --git a/trunk/src/app/srs_app_http_conn.hpp b/trunk/src/app/srs_app_http_conn.hpp index bb0370c96..1bb91fea9 100644 --- a/trunk/src/app/srs_app_http_conn.hpp +++ b/trunk/src/app/srs_app_http_conn.hpp @@ -49,8 +49,9 @@ public: 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); +protected: + virtual bool is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase); virtual int do_process_request(SrsSocket* skt, SrsHttpMessage* req); }; @@ -65,6 +66,8 @@ public: virtual ~SrsHttpVhost(); public: virtual bool can_handle(const char* path, int length, const char** pchild); +protected: + virtual bool is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase); virtual int do_process_request(SrsSocket* skt, SrsHttpMessage* req); public: virtual std::string vhost(); diff --git a/trunk/src/core/srs_core.cpp b/trunk/src/core/srs_core.cpp index bfa353dec..92918ff7a 100644 --- a/trunk/src/core/srs_core.cpp +++ b/trunk/src/core/srs_core.cpp @@ -85,6 +85,11 @@ string srs_string_remove(string str, string remove_chars) return ret; } +bool srs_string_ends_with(string str, string flag) +{ + return str.rfind(flag) == str.length() - flag.length(); +} + string srs_dns_resolve(string host) { if (inet_addr(host.c_str()) != INADDR_NONE) { diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 8a9325411..a3a7ac604 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 "0" #define VERSION_MINOR "9" -#define VERSION_REVISION "48" +#define VERSION_REVISION "49" #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION // server info. #define RTMP_SIG_SRS_KEY "srs" @@ -97,6 +97,8 @@ extern std::string srs_string_replace(std::string str, std::string old_str, std: extern std::string srs_string_trim_end(std::string str, std::string trim_chars); // remove char in remove_chars of str extern std::string srs_string_remove(std::string str, std::string remove_chars); +// whether string end with +extern bool srs_string_ends_with(std::string str, std::string flag); // dns resolve utility, return the resolved ip address. extern std::string srs_dns_resolve(std::string host); diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index c898e32f3..fde84f252 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -159,6 +159,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_HTTP_PARSE_HEADER 802 #define ERROR_HTTP_HANDLER_MATCH_URL 803 #define ERROR_HTTP_HANDLER_INVALID 804 +#define ERROR_HTTP_OPEN_FILE 805 +#define ERROR_HTTP_READ_FILE 806 // system control message, // not an error, but special control logic.