diff --git a/README.md b/README.md
index 4cde2a35c..1222b0396 100755
--- a/README.md
+++ b/README.md
@@ -46,6 +46,7 @@ url: rtmp://127.0.0.1:1935/live/livestream
* nginx v1.5.0: 139524 lines
### History
+* v0.3, 2013-11-04, support vhosts specified config.
* v0.3, 2013-11-02, support listen multiple ports.
* v0.3, 2013-11-02, support config file in nginx-conf style.
* v0.3, 2013-10-29, support pithy print log message specified by stage.
diff --git a/trunk/conf/srs.conf b/trunk/conf/srs.conf
index 1af614102..36702d7c9 100755
--- a/trunk/conf/srs.conf
+++ b/trunk/conf/srs.conf
@@ -1,7 +1,12 @@
listen 1935 19350;
vhost __defaultVhost__ {
- application live {
- no_delay on;
- allow all;
- }
+ enabled on;
+ gop_cache on;
+ #refer github.com github.io;
+ #refer_publish github.com github.io;
+ refer_play github.com github.io .com;
}
+vhost winlinvip.github.com {
+ gop_cache off;
+}
+
diff --git a/trunk/configure b/trunk/configure
index ad23cb03b..0149d34c7 100755
--- a/trunk/configure
+++ b/trunk/configure
@@ -91,7 +91,7 @@ MODULE_FILES=("srs_core" "srs_core_log" "srs_core_server"
"srs_core_auto_free" "srs_core_protocol" "srs_core_amf0"
"srs_core_stream" "srs_core_source" "srs_core_codec"
"srs_core_complex_handshake" "srs_core_pithy_print"
- "srs_core_config")
+ "srs_core_config" "srs_core_refer")
MODULE_DIR="src/core" . auto/modules.sh
CORE_OBJS="${MODULE_OBJS[@]}"
diff --git a/trunk/src/core/srs_core_client.cpp b/trunk/src/core/srs_core_client.cpp
index 782e0a872..1c1118c25 100755
--- a/trunk/src/core/srs_core_client.cpp
+++ b/trunk/src/core/srs_core_client.cpp
@@ -33,6 +33,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include
#include
#include
+#include
+#include
#define SRS_PULSE_TIMEOUT_MS 100
#define SRS_SEND_TIMEOUT_MS 5000000L
@@ -45,6 +47,7 @@ SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd)
req = new SrsRequest();
res = new SrsResponse();
rtmp = new SrsRtmp(client_stfd);
+ refer = new SrsRefer();
}
SrsClient::~SrsClient()
@@ -53,6 +56,7 @@ SrsClient::~SrsClient()
srs_freep(req);
srs_freep(res);
srs_freep(rtmp);
+ srs_freep(refer);
}
int SrsClient::do_cycle()
@@ -79,11 +83,25 @@ int SrsClient::do_cycle()
srs_error("rtmp connect vhost/app failed. ret=%d", ret);
return ret;
}
+ srs_verbose("rtmp connect app success");
+
+ if ((ret = check_vhost()) != ERROR_SUCCESS) {
+ srs_error("check vhost failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("check vhost success.");
+
srs_trace("rtmp connect app success. "
"tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s",
req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(),
req->schema.c_str(), req->vhost.c_str(), req->port.c_str(),
req->app.c_str());
+
+ if ((ret = refer->check(req->pageUrl, config->get_refer(req->vhost))) != ERROR_SUCCESS) {
+ srs_error("check refer failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("check refer success.");
if ((ret = rtmp->set_window_ack_size(2.5 * 1000 * 1000)) != ERROR_SUCCESS) {
srs_error("set window acknowledgement size failed. ret=%d", ret);
@@ -114,7 +132,8 @@ int SrsClient::do_cycle()
srs_error("identify client failed. ret=%d", ret);
return ret;
}
- srs_verbose("identify client success. type=%d, stream_name=%s", type, req->stream.c_str());
+ req->strip();
+ srs_trace("identify client success. type=%d, stream_name=%s", type, req->stream.c_str());
// TODO: read from config.
int chunk_size = 4096;
@@ -127,7 +146,15 @@ int SrsClient::do_cycle()
// find a source to publish.
SrsSource* source = SrsSource::find(req->get_stream_url());
srs_assert(source != NULL);
- srs_info("source found, url=%s", req->get_stream_url().c_str());
+
+ SrsConfDirective* conf = config->get_gop_cache(req->vhost);
+ bool enabled_cache = true;
+ if (conf && conf->arg0() == "off") {
+ enabled_cache = false;
+ }
+ source->set_cache(enabled_cache);
+
+ srs_info("source found, url=%s, enabled_cache=%d", req->get_stream_url().c_str(), enabled_cache);
switch (type) {
case SrsClientPlay: {
@@ -174,10 +201,44 @@ int SrsClient::do_cycle()
return ret;
}
+int SrsClient::check_vhost()
+{
+ int ret = ERROR_SUCCESS;
+
+ srs_assert(req != NULL);
+
+ SrsConfDirective* vhost = config->get_vhost(req->vhost);
+ if (vhost == NULL) {
+ ret = ERROR_RTMP_VHOST_NOT_FOUND;
+ srs_error("vhost %s not found. ret=%d", req->vhost.c_str(), ret);
+ return ret;
+ }
+
+ SrsConfDirective* conf = NULL;
+ if ((conf = vhost->get(RTMP_VHOST_ENABLED)) != NULL && conf->arg0() == "off") {
+ ret = ERROR_RTMP_VHOST_NOT_FOUND;
+ srs_error("vhost %s disabled. ret=%d", req->vhost.c_str(), ret);
+ return ret;
+ }
+
+ if (req->vhost != vhost->arg0()) {
+ srs_trace("vhost change from %s to %s", req->vhost.c_str(), vhost->arg0().c_str());
+ req->vhost = vhost->arg0();
+ }
+
+ return ret;
+}
+
int SrsClient::playing(SrsSource* source)
{
int ret = ERROR_SUCCESS;
+ if ((ret = refer->check(req->pageUrl, config->get_refer_play(req->vhost))) != ERROR_SUCCESS) {
+ srs_error("check play_refer failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("check play_refer success.");
+
SrsConsumer* consumer = NULL;
if ((ret = source->create_consumer(consumer)) != ERROR_SUCCESS) {
srs_error("create consumer failed. ret=%d", ret);
@@ -258,6 +319,12 @@ int SrsClient::publish(SrsSource* source, bool is_fmle)
{
int ret = ERROR_SUCCESS;
+ if ((ret = refer->check(req->pageUrl, config->get_refer_publish(req->vhost))) != ERROR_SUCCESS) {
+ srs_error("check publish_refer failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("check publish_refer success.");
+
SrsPithyPrint pithy_print(SRS_STAGE_PUBLISH_USER);
while (true) {
diff --git a/trunk/src/core/srs_core_client.hpp b/trunk/src/core/srs_core_client.hpp
index 763d3ecec..dab54e170 100755
--- a/trunk/src/core/srs_core_client.hpp
+++ b/trunk/src/core/srs_core_client.hpp
@@ -36,6 +36,7 @@ class SrsRtmp;
class SrsRequest;
class SrsResponse;
class SrsSource;
+class SrsRefer;
/**
* the client provides the main logic control for RTMP clients.
@@ -47,12 +48,14 @@ private:
SrsRequest* req;
SrsResponse* res;
SrsRtmp* rtmp;
+ SrsRefer* refer;
public:
SrsClient(SrsServer* srs_server, st_netfd_t client_stfd);
virtual ~SrsClient();
protected:
virtual int do_cycle();
private:
+ virtual int check_vhost();
virtual int playing(SrsSource* source);
virtual int publish(SrsSource* source, bool is_fmle);
virtual int get_peer_ip();
diff --git a/trunk/src/core/srs_core_config.cpp b/trunk/src/core/srs_core_config.cpp
index 0aa3ba6fe..3a183e14b 100755
--- a/trunk/src/core/srs_core_config.cpp
+++ b/trunk/src/core/srs_core_config.cpp
@@ -102,6 +102,33 @@ SrsConfDirective::~SrsConfDirective()
directives.clear();
}
+std::string SrsConfDirective::arg0()
+{
+ if (args.size() > 0) {
+ return args.at(0);
+ }
+
+ return "";
+}
+
+std::string SrsConfDirective::arg1()
+{
+ if (args.size() > 1) {
+ return args.at(1);
+ }
+
+ return "";
+}
+
+std::string SrsConfDirective::arg2()
+{
+ if (args.size() > 2) {
+ return args.at(2);
+ }
+
+ return "";
+}
+
SrsConfDirective* SrsConfDirective::at(int index)
{
return directives.at(index);
@@ -437,6 +464,73 @@ int Config::parse_options(int argc, char** argv)
return ret;
}
+SrsConfDirective* Config::get_vhost(std::string vhost)
+{
+ srs_assert(root);
+
+ for (int i = 0; i < (int)root->directives.size(); i++) {
+ SrsConfDirective* conf = root->at(i);
+
+ if (conf->name != "vhost") {
+ continue;
+ }
+
+ if (conf->arg0() == vhost) {
+ return conf;
+ }
+ }
+
+ if (vhost != RTMP_VHOST_DEFAULT) {
+ return get_vhost(RTMP_VHOST_DEFAULT);
+ }
+
+ return NULL;
+}
+
+SrsConfDirective* Config::get_gop_cache(std::string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ return conf->get("gop_cache");
+}
+
+SrsConfDirective* Config::get_refer(std::string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ return conf->get("refer");
+}
+
+SrsConfDirective* Config::get_refer_play(std::string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ return conf->get("refer_play");
+}
+
+SrsConfDirective* Config::get_refer_publish(std::string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ return conf->get("refer_publish");
+}
+
SrsConfDirective* Config::get_listen()
{
return root->get("listen");
diff --git a/trunk/src/core/srs_core_config.hpp b/trunk/src/core/srs_core_config.hpp
index 323690e4a..189e8411e 100755
--- a/trunk/src/core/srs_core_config.hpp
+++ b/trunk/src/core/srs_core_config.hpp
@@ -32,6 +32,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include
#include
+// default vhost for rtmp
+#define RTMP_VHOST_DEFAULT "__defaultVhost__"
+
+// conf node: enabled.
+#define RTMP_VHOST_ENABLED "enabled"
+
class SrsFileBuffer
{
public:
@@ -61,6 +67,9 @@ public:
public:
SrsConfDirective();
virtual ~SrsConfDirective();
+ std::string arg0();
+ std::string arg1();
+ std::string arg2();
SrsConfDirective* at(int index);
SrsConfDirective* get(std::string _name);
public:
@@ -87,6 +96,11 @@ public:
virtual ~Config();
public:
virtual int parse_options(int argc, char** argv);
+ virtual SrsConfDirective* get_vhost(std::string vhost);
+ virtual SrsConfDirective* get_gop_cache(std::string vhost);
+ virtual SrsConfDirective* get_refer(std::string vhost);
+ virtual SrsConfDirective* get_refer_play(std::string vhost);
+ virtual SrsConfDirective* get_refer_publish(std::string vhost);
virtual SrsConfDirective* get_listen();
private:
virtual int parse_argv(int& i, char** argv);
diff --git a/trunk/src/core/srs_core_error.hpp b/trunk/src/core/srs_core_error.hpp
index e51cde12b..6dca9bbee 100755
--- a/trunk/src/core/srs_core_error.hpp
+++ b/trunk/src/core/srs_core_error.hpp
@@ -65,6 +65,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define ERROR_RTMP_TRY_SIMPLE_HS 311
#define ERROR_RTMP_CH_SCHEMA 312
#define ERROR_RTMP_PACKET_SIZE 313
+#define ERROR_RTMP_VHOST_NOT_FOUND 314
+#define ERROR_RTMP_ACCESS_DENIED 315
#define ERROR_SYSTEM_STREAM_INIT 400
#define ERROR_SYSTEM_PACKET_INVALID 401
diff --git a/trunk/src/core/srs_core_refer.cpp b/trunk/src/core/srs_core_refer.cpp
new file mode 100755
index 000000000..7fd1aa860
--- /dev/null
+++ b/trunk/src/core/srs_core_refer.cpp
@@ -0,0 +1,88 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2013 winlin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include
+
+#include
+#include
+#include
+
+int SrsRefer::check(std::string page_url, SrsConfDirective* refer)
+{
+ int ret = ERROR_SUCCESS;
+
+ if (!refer) {
+ srs_verbose("ignore refer check for page_url=%s", page_url.c_str());
+ return ret;
+ }
+
+ for (int i = 0; i < (int)refer->args.size(); i++) {
+ if ((ret = check_single_refer(page_url, refer->args.at(i))) == ERROR_SUCCESS) {
+ srs_verbose("check refer success. page_url=%s, refer=%s",
+ page_url.c_str(), refer->args.at(i).c_str());
+ return ret;
+ }
+ }
+
+ ret = ERROR_RTMP_ACCESS_DENIED;
+ srs_error("check refer failed. ret=%d", ret);
+
+ return ret;
+}
+
+int SrsRefer::check_single_refer(std::string page_url, std::string refer)
+{
+ int ret = ERROR_SUCCESS;
+
+ size_t pos = std::string::npos;
+
+ std::string domain_name = page_url;
+ if ((pos = domain_name.find("://")) != std::string::npos) {
+ domain_name = domain_name.substr(pos + 3);
+ }
+
+ if ((pos = domain_name.find("/")) != std::string::npos) {
+ domain_name = domain_name.substr(0, pos);
+ }
+
+ if ((pos = domain_name.find(":")) != std::string::npos) {
+ domain_name = domain_name.substr(0, pos);
+ }
+
+ pos = domain_name.find(refer);
+ if (pos == std::string::npos) {
+ ret = ERROR_RTMP_ACCESS_DENIED;
+ }
+ // match primary domain.
+ if (pos != domain_name.length() - refer.length()) {
+ ret = ERROR_RTMP_ACCESS_DENIED;
+ }
+
+ if (ret != ERROR_SUCCESS) {
+ srs_verbose("access denied, page_url=%s, domain_name=%s, refer=%s, ret=%d",
+ page_url.c_str(), domain_name.c_str(), refer.c_str(), ret);
+ }
+
+ return ret;
+}
+
diff --git a/trunk/src/core/srs_core_refer.hpp b/trunk/src/core/srs_core_refer.hpp
new file mode 100755
index 000000000..aeab41733
--- /dev/null
+++ b/trunk/src/core/srs_core_refer.hpp
@@ -0,0 +1,49 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2013 winlin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef SRS_CORE_REFER_HPP
+#define SRS_CORE_REFER_HPP
+
+/*
+#include
+*/
+#include
+
+#include
+
+class SrsConfDirective;
+
+class SrsRefer
+{
+public:
+ /**
+ * to check the refer.
+ * @param page_url the client page url.
+ * @param refer the refer in config.
+ */
+ virtual int check(std::string page_url, SrsConfDirective* refer);
+private:
+ virtual int check_single_refer(std::string page_url, std::string refer);
+};
+
+#endif
\ No newline at end of file
diff --git a/trunk/src/core/srs_core_rtmp.cpp b/trunk/src/core/srs_core_rtmp.cpp
index 1d1907e21..db777f149 100755
--- a/trunk/src/core/srs_core_rtmp.cpp
+++ b/trunk/src/core/srs_core_rtmp.cpp
@@ -110,6 +110,8 @@ int SrsRequest::discovery_app()
return ret;
}
+ strip();
+
return ret;
}
@@ -117,8 +119,7 @@ std::string SrsRequest::get_stream_url()
{
std::string url = "";
- //url += vhost;
-
+ url += vhost;
url += "/";
url += app;
url += "/";
@@ -127,6 +128,30 @@ std::string SrsRequest::get_stream_url()
return url;
}
+void SrsRequest::strip()
+{
+ trim(vhost, "/ \n\r\t");
+ trim(app, "/ \n\r\t");
+ trim(stream, "/ \n\r\t");
+}
+
+std::string& SrsRequest::trim(std::string& str, std::string chs)
+{
+ for (int i = 0; i < (int)chs.length(); i++) {
+ char ch = chs.at(i);
+
+ for (std::string::iterator it = str.begin(); it != str.end();) {
+ if (ch == *it) {
+ it = str.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ return str;
+}
+
SrsResponse::SrsResponse()
{
stream_id = SRS_DEFAULT_SID;
diff --git a/trunk/src/core/srs_core_rtmp.hpp b/trunk/src/core/srs_core_rtmp.hpp
index 8ea3d9cce..547082379 100755
--- a/trunk/src/core/srs_core_rtmp.hpp
+++ b/trunk/src/core/srs_core_rtmp.hpp
@@ -66,6 +66,9 @@ struct SrsRequest
*/
virtual int discovery_app();
virtual std::string get_stream_url();
+ virtual void strip();
+private:
+ std::string& trim(std::string& str, std::string chs);
};
/**
diff --git a/trunk/src/core/srs_core_source.cpp b/trunk/src/core/srs_core_source.cpp
index 9349fb13d..53c288e66 100755
--- a/trunk/src/core/srs_core_source.cpp
+++ b/trunk/src/core/srs_core_source.cpp
@@ -391,6 +391,19 @@ void SrsSource::on_unpublish()
srs_trace("clear cache/metadata/sequence-headers when unpublish.");
}
+void SrsSource::set_cache(bool enabled)
+{
+ enable_gop_cache = enabled;
+
+ if (!enabled) {
+ srs_info("disable gop cache, clear %d packets.", (int)gop_cache.size());
+ clear_gop_cache();
+ return;
+ }
+
+ srs_info("enable gop cache");
+}
+
int SrsSource::cache_last_gop(SrsSharedPtrMessage* msg)
{
int ret = ERROR_SUCCESS;
diff --git a/trunk/src/core/srs_core_source.hpp b/trunk/src/core/srs_core_source.hpp
index 30f2cfa6f..9b14002de 100755
--- a/trunk/src/core/srs_core_source.hpp
+++ b/trunk/src/core/srs_core_source.hpp
@@ -134,6 +134,7 @@ public:
virtual int create_consumer(SrsConsumer*& consumer);
virtual void on_consumer_destroy(SrsConsumer* consumer);
virtual void on_unpublish();
+ virtual void set_cache(bool enabled);
private:
/**
* only for h264 codec
diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp
index a9120396b..887601492 100755
--- a/trunk/src/srs/srs.upp
+++ b/trunk/src/srs/srs.upp
@@ -12,6 +12,8 @@ file
..\core\srs_core_server.cpp,
..\core\srs_core_config.hpp,
..\core\srs_core_config.cpp,
+ ..\core\srs_core_refer.hpp,
+ ..\core\srs_core_refer.cpp,
..\core\srs_core_conn.hpp,
..\core\srs_core_conn.cpp,
..\core\srs_core_client.hpp,