diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 23d439a5c..a9aa05635 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -842,9 +842,9 @@ bool SrsConfDirective::is_stream_caster() return name == "stream_caster"; } -srs_error_t SrsConfDirective::parse(SrsConfigBuffer* buffer) +srs_error_t SrsConfDirective::parse(SrsConfigBuffer* buffer, SrsConfig* conf) { - return parse_conf(buffer, parse_file); + return parse_conf(buffer, SrsDirectiveContextFile, conf); } srs_error_t SrsConfDirective::persistence(SrsFileWriter* writer, int level) @@ -971,71 +971,78 @@ SrsJsonAny* SrsConfDirective::dumps_arg0_to_boolean() // LCOV_EXCL_STOP // see: ngx_conf_parse -srs_error_t SrsConfDirective::parse_conf(SrsConfigBuffer* buffer, SrsDirectiveType type) +srs_error_t SrsConfDirective::parse_conf(SrsConfigBuffer* buffer, SrsDirectiveContext ctx, SrsConfig* conf) { srs_error_t err = srs_success; while (true) { std::vector args; int line_start = 0; - err = read_token(buffer, args, line_start); - - /** - * ret maybe: - * ERROR_SYSTEM_CONFIG_INVALID error. - * ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found - * ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found - * ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found - * ERROR_SYSTEM_CONFIG_EOF the config file is done - */ - if (srs_error_code(err) == ERROR_SYSTEM_CONFIG_INVALID) { - return err; + SrsDirectiveState state = SrsDirectiveStateInit; + if ((err = read_token(buffer, args, line_start, state)) != srs_success) { + return srs_error_wrap(err, "read token, line=%d, state=%d", line_start, state); } - if (srs_error_code(err) == ERROR_SYSTEM_CONFIG_BLOCK_END) { - if (type != parse_block) { - return srs_error_wrap(err, "line %d: unexpected \"}\"", buffer->line); - } - - srs_freep(err); - return srs_success; + + if (state == SrsDirectiveStateBlockEnd) { + return ctx == SrsDirectiveContextBlock ? srs_success : srs_error_wrap(err, "line %d: unexpected \"}\"", buffer->line); } - if (srs_error_code(err) == ERROR_SYSTEM_CONFIG_EOF) { - if (type == parse_block) { - return srs_error_wrap(err, "line %d: unexpected end of file, expecting \"}\"", conf_line); - } - - srs_freep(err); - return srs_success; + if (state == SrsDirectiveStateEOF) { + return ctx != SrsDirectiveContextBlock ? srs_success : srs_error_wrap(err, "line %d: unexpected end of file, expecting \"}\"", conf_line); } - if (args.empty()) { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: empty directive", conf_line); } - // build directive tree. - SrsConfDirective* directive = new SrsConfDirective(); - - directive->conf_line = line_start; - directive->name = args[0]; - args.erase(args.begin()); - directive->args.swap(args); - - directives.push_back(directive); - - if (srs_error_code(err) == ERROR_SYSTEM_CONFIG_BLOCK_START) { - srs_freep(err); - if ((err = directive->parse_conf(buffer, parse_block)) != srs_success) { - return srs_error_wrap(err, "parse dir"); + // Build normal directive which is not "include". + if (args.at(0) != "include") { + SrsConfDirective* directive = new SrsConfDirective(); + + directive->conf_line = line_start; + directive->name = args[0]; + args.erase(args.begin()); + directive->args.swap(args); + + directives.push_back(directive); + + if (state == SrsDirectiveStateBlockStart) { + if ((err = directive->parse_conf(buffer, SrsDirectiveContextBlock, conf)) != srs_success) { + return srs_error_wrap(err, "parse dir"); + } + } + continue; + } + + // Parse including, allow multiple files. + vector files(args.begin() + 1, args.end()); + if (files.empty()) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: include is empty directive", buffer->line); + } + if (!conf) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: no config", buffer->line); + } + + for (int i = 0; i < (int)files.size(); i++) { + std::string file = files.at(i); + srs_assert(!file.empty()); + srs_trace("config parse include %s", file.c_str()); + + SrsConfigBuffer* include_file_buffer = NULL; + SrsAutoFree(SrsConfigBuffer, include_file_buffer); + if ((err = conf->build_buffer(file, &include_file_buffer)) != srs_success) { + return srs_error_wrap(err, "buffer fullfill %s", file.c_str()); + } + + if ((err = parse_conf(include_file_buffer, SrsDirectiveContextFile, conf)) != srs_success) { + return srs_error_wrap(err, "parse include buffer"); } } - srs_freep(err); } return err; } // see: ngx_conf_read_token -srs_error_t SrsConfDirective::read_token(SrsConfigBuffer* buffer, vector& args, int& line_start) +srs_error_t SrsConfDirective::read_token(SrsConfigBuffer* buffer, vector& args, int& line_start, SrsDirectiveState& state) { srs_error_t err = srs_success; @@ -1057,8 +1064,9 @@ srs_error_t SrsConfDirective::read_token(SrsConfigBuffer* buffer, vector buffer->line); } srs_trace("config parse complete"); - - return srs_error_new(ERROR_SYSTEM_CONFIG_EOF, "EOF"); + + state = SrsDirectiveStateEOF; + return err; } char ch = *buffer->pos++; @@ -1079,10 +1087,12 @@ srs_error_t SrsConfDirective::read_token(SrsConfigBuffer* buffer, vector continue; } if (ch == ';') { - return srs_error_new(ERROR_SYSTEM_CONFIG_DIRECTIVE, "dir"); + state = SrsDirectiveStateEntire; + return err; } if (ch == '{') { - return srs_error_new(ERROR_SYSTEM_CONFIG_BLOCK_START, "block"); + state = SrsDirectiveStateBlockStart; + return err; } return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: unexpected '%c'", buffer->line, ch); } @@ -1098,17 +1108,20 @@ srs_error_t SrsConfDirective::read_token(SrsConfigBuffer* buffer, vector if (args.size() == 0) { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: unexpected ';'", buffer->line); } - return srs_error_new(ERROR_SYSTEM_CONFIG_DIRECTIVE, "dir"); + state = SrsDirectiveStateEntire; + return err; case '{': if (args.size() == 0) { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: unexpected '{'", buffer->line); } - return srs_error_new(ERROR_SYSTEM_CONFIG_BLOCK_START, "block"); + state = SrsDirectiveStateBlockStart; + return err; case '}': if (args.size() != 0) { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: unexpected '}'", buffer->line); } - return srs_error_new(ERROR_SYSTEM_CONFIG_BLOCK_END, "block"); + state = SrsDirectiveStateBlockEnd; + return err; case '#': sharp_comment = 1; continue; @@ -1163,10 +1176,12 @@ srs_error_t SrsConfDirective::read_token(SrsConfigBuffer* buffer, vector srs_freepa(aword); if (ch == ';') { - return srs_error_new(ERROR_SYSTEM_CONFIG_DIRECTIVE, "dir"); + state = SrsDirectiveStateEntire; + return err; } if (ch == '{') { - return srs_error_new(ERROR_SYSTEM_CONFIG_BLOCK_START, "block"); + state = SrsDirectiveStateBlockStart; + return err; } } } @@ -2406,19 +2421,34 @@ srs_error_t SrsConfig::parse_file(const char* filename) if (config_file.empty()) { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "empty config"); } - - SrsConfigBuffer buffer; - - if ((err = buffer.fullfill(config_file.c_str())) != srs_success) { - return srs_error_wrap(err, "buffer fullfil"); + + SrsConfigBuffer* buffer = NULL; + SrsAutoFree(SrsConfigBuffer, buffer); + if ((err = build_buffer(config_file, &buffer)) != srs_success) { + return srs_error_wrap(err, "buffer fullfill %s", config_file.c_str()); } - if ((err = parse_buffer(&buffer)) != srs_success) { + if ((err = parse_buffer(buffer)) != srs_success) { return srs_error_wrap(err, "parse buffer"); } return err; } + +srs_error_t SrsConfig::build_buffer(string src, SrsConfigBuffer** pbuffer) +{ + srs_error_t err = srs_success; + + SrsConfigBuffer* buffer = new SrsConfigBuffer(); + + if ((err = buffer->fullfill(src.c_str())) != srs_success) { + srs_freep(buffer); + return srs_error_wrap(err, "read from src %s", src.c_str()); + } + + *pbuffer = buffer; + return err; +} // LCOV_EXCL_STOP srs_error_t SrsConfig::check_config() @@ -2955,7 +2985,7 @@ srs_error_t SrsConfig::parse_buffer(SrsConfigBuffer* buffer) root = new SrsConfDirective(); // Parse root tree from buffer. - if ((err = root->parse(buffer)) != srs_success) { + if ((err = root->parse(buffer, this)) != srs_success) { return srs_error_wrap(err, "root parse"); } diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index f48d71a6d..7623dc04d 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -210,7 +210,7 @@ public: // Parse utilities public: // Parse config directive from file buffer. - virtual srs_error_t parse(srs_internal::SrsConfigBuffer* buffer); + virtual srs_error_t parse(srs_internal::SrsConfigBuffer* buffer, SrsConfig* conf = NULL); // Marshal the directive to writer. // @param level, the root is level0, all its directives are level1, and so on. virtual srs_error_t persistence(SrsFileWriter* writer, int level); @@ -223,24 +223,36 @@ public: virtual SrsJsonAny* dumps_arg0_to_boolean(); // private parse. private: - // The directive parsing type. - enum SrsDirectiveType { + // The directive parsing context. + enum SrsDirectiveContext { // The root directives, parsing file. - parse_file, - // For each direcitve, parsing text block. - parse_block + SrsDirectiveContextFile, + // For each directive, parsing text block. + SrsDirectiveContextBlock, + }; + enum SrsDirectiveState { + // Init state + SrsDirectiveStateInit, + // The directive terminated by ';' found + SrsDirectiveStateEntire, + // The token terminated by '{' found + SrsDirectiveStateBlockStart, + // The '}' found + SrsDirectiveStateBlockEnd, + // The config file is done + SrsDirectiveStateEOF, }; // Parse the conf from buffer. the work flow: // 1. read a token(directive args and a ret flag), // 2. initialize the directive by args, args[0] is name, args[1-N] is args of directive, // 3. if ret flag indicates there are child-directives, read_conf(directive, block) recursively. - virtual srs_error_t parse_conf(srs_internal::SrsConfigBuffer* buffer, SrsDirectiveType type); + virtual srs_error_t parse_conf(srs_internal::SrsConfigBuffer* buffer, SrsDirectiveContext ctx, SrsConfig* conf); // Read a token from buffer. // A token, is the directive args and a flag indicates whether has child-directives. // @param args, the output directive args, the first is the directive name, left is the args. // @param line_start, the actual start line of directive. // @return, an error code indicates error or has child-directives. - virtual srs_error_t read_token(srs_internal::SrsConfigBuffer* buffer, std::vector& args, int& line_start); + virtual srs_error_t read_token(srs_internal::SrsConfigBuffer* buffer, std::vector& args, int& line_start, SrsDirectiveState& state); }; // The config service provider. @@ -250,6 +262,7 @@ private: // You could keep it before st-thread switch, or simply never keep it. class SrsConfig { + friend class SrsConfDirective; // user command private: // Whether srs is run in dolphin mode. @@ -356,6 +369,10 @@ private: public: // Parse the config file, which is specified by cli. virtual srs_error_t parse_file(const char* filename); +private: + // Build a buffer from a src, which is string content or filename. + virtual srs_error_t build_buffer(std::string src, srs_internal::SrsConfigBuffer** pbuffer); +public: // Check the parsed config. virtual srs_error_t check_config(); protected: diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 722ca60b4..c07bb84c8 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -43,10 +43,6 @@ #define ERROR_SYSTEM_ASSERT_FAILED 1021 #define ERROR_READER_BUFFER_OVERFLOW 1022 #define ERROR_SYSTEM_CONFIG_INVALID 1023 -#define ERROR_SYSTEM_CONFIG_DIRECTIVE 1024 -#define ERROR_SYSTEM_CONFIG_BLOCK_START 1025 -#define ERROR_SYSTEM_CONFIG_BLOCK_END 1026 -#define ERROR_SYSTEM_CONFIG_EOF 1027 #define ERROR_SYSTEM_STREAM_BUSY 1028 #define ERROR_SYSTEM_IP_INVALID 1029 #define ERROR_SYSTEM_FORWARD_LOOP 1030 diff --git a/trunk/src/utest/srs_utest_config.cpp b/trunk/src/utest/srs_utest_config.cpp index e5532d678..3a3c04a5b 100644 --- a/trunk/src/utest/srs_utest_config.cpp +++ b/trunk/src/utest/srs_utest_config.cpp @@ -71,6 +71,29 @@ srs_error_t MockSrsConfig::parse(string buf) return err; } +srs_error_t MockSrsConfig::mock_include(const string file_name, const string content) +{ + srs_error_t err = srs_success; + + included_files[file_name] = content; + + return err; +} + +srs_error_t MockSrsConfig::build_buffer(std::string src, srs_internal::SrsConfigBuffer** pbuffer) +{ + srs_error_t err = srs_success; + + string content = included_files[src]; + if(content.empty()) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "file %s: no found", src.c_str()); + } + + *pbuffer = new MockSrsConfigBuffer(content); + + return err; +} + VOID TEST(ConfigTest, CheckMacros) { #ifndef SRS_CONSTS_LOCALHOST @@ -303,6 +326,63 @@ VOID TEST(ConfigDirectiveTest, ParseNameArg2_Dir0Arg0_Dir0Arg0) EXPECT_EQ(0, (int)ddir0.directives.size()); } +VOID TEST(ConfigDirectiveTest, ParseArgsSpace) +{ + srs_error_t err; + + if (true) { + vector usecases; + usecases.push_back("include;"); + usecases.push_back("include ;"); + usecases.push_back("include ;"); + usecases.push_back("include ;");; + usecases.push_back("include\r;"); + usecases.push_back("include\n;"); + usecases.push_back("include \r \n \r\n \n\r;"); + for (int i = 0; i < (int)usecases.size(); i++) { + string usecase = usecases.at(i); + + MockSrsConfigBuffer buf(usecase); + SrsConfDirective conf; + HELPER_ASSERT_FAILED(conf.parse(&buf)); + EXPECT_EQ(0, (int) conf.name.length()); + EXPECT_EQ(0, (int) conf.args.size()); + EXPECT_EQ(0, (int) conf.directives.size()); + } + } + + if (true) { + vector usecases; + usecases.push_back("include test;"); + usecases.push_back("include test;"); + usecases.push_back("include test;"); + usecases.push_back("include test;");; + usecases.push_back("include\rtest;"); + usecases.push_back("include\ntest;"); + usecases.push_back("include \r \n \r\n \n\rtest;"); + + MockSrsConfig config; + config.mock_include("test", "listen 1935;"); + + for (int i = 0; i < (int)usecases.size(); i++) { + string usecase = usecases.at(i); + + MockSrsConfigBuffer buf(usecase); + SrsConfDirective conf; + HELPER_ASSERT_SUCCESS(conf.parse(&buf, &config)); + EXPECT_EQ(0, (int) conf.name.length()); + EXPECT_EQ(0, (int) conf.args.size()); + EXPECT_EQ(1, (int) conf.directives.size()); + + SrsConfDirective &dir = *conf.directives.at(0); + EXPECT_STREQ("listen", dir.name.c_str()); + EXPECT_EQ(1, (int) dir.args.size()); + EXPECT_STREQ("1935", dir.arg0().c_str()); + EXPECT_EQ(0, (int) dir.directives.size()); + } + } +} + VOID TEST(ConfigDirectiveTest, Parse2SingleDirs) { srs_error_t err; @@ -3622,3 +3702,190 @@ VOID TEST(ConfigMainTest, CheckVhostConfig5) } } +VOID TEST(ConfigMainTest, CheckIncludeConfig) +{ + srs_error_t err; + + if (true) { + MockSrsConfig conf; + + conf.mock_include("./conf/include_test/include.conf", "listen 1935;include ./conf/include_test/include_1.conf;"); + conf.mock_include("./conf/include_test/include_1.conf", "max_connections 1000;daemon off;srs_log_tank console;http_server {enabled on;listen xxx;dir xxx2;}vhost ossrs.net {hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}}"); + + HELPER_ASSERT_SUCCESS(conf.parse("include ./conf/include_test/include.conf;")); + + vector listens = conf.get_listens(); + EXPECT_EQ(1, (int)listens.size()); + EXPECT_STREQ("1935", listens.at(0).c_str()); + + EXPECT_FALSE(conf.get_log_tank_file()); + + EXPECT_TRUE(conf.get_http_stream_enabled()); + EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); + EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str()); + + EXPECT_TRUE(conf.get_hls_enabled("ossrs.net")); + EXPECT_STREQ("xxx", conf.get_hls_path("ossrs.net").c_str()); + EXPECT_STREQ("xxx1", conf.get_hls_m3u8_file("ossrs.net").c_str()); + EXPECT_STREQ("xxx2", conf.get_hls_ts_file("ossrs.net").c_str()); + EXPECT_EQ(10*SRS_UTIME_SECONDS, conf.get_hls_fragment("ossrs.net")); + EXPECT_EQ(60*SRS_UTIME_SECONDS, conf.get_hls_window("ossrs.net")); + } + + if (true) { + MockSrsConfig conf; + + conf.mock_include("./conf/include_test/include_1.conf", "max_connections 1000;daemon off;srs_log_tank console;http_server {enabled on;listen xxx;dir xxx2;}vhost ossrs.net {hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}}"); + + HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "include ./conf/include_test/include_1.conf;")); + + vector listens = conf.get_listens(); + EXPECT_EQ(1, (int)listens.size()); + EXPECT_STREQ("1935", listens.at(0).c_str()); + + EXPECT_FALSE(conf.get_log_tank_file()); + + EXPECT_TRUE(conf.get_http_stream_enabled()); + EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); + EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str()); + + EXPECT_TRUE(conf.get_hls_enabled("ossrs.net")); + EXPECT_STREQ("xxx", conf.get_hls_path("ossrs.net").c_str()); + EXPECT_STREQ("xxx1", conf.get_hls_m3u8_file("ossrs.net").c_str()); + EXPECT_STREQ("xxx2", conf.get_hls_ts_file("ossrs.net").c_str()); + EXPECT_EQ(10*SRS_UTIME_SECONDS, conf.get_hls_fragment("ossrs.net")); + EXPECT_EQ(60*SRS_UTIME_SECONDS, conf.get_hls_window("ossrs.net")); + } + + if (true) { + MockSrsConfig conf; + + conf.mock_include("./conf/include_test/include_2.conf", "listen 1935;max_connections 1000;daemon off;srs_log_tank console;http_server {enabled on;listen xxx;dir xxx2;}vhost ossrs.net {include ./conf/include_test/include_3.conf;}"); + conf.mock_include("./conf/include_test/include_3.conf", "hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}"); + + HELPER_ASSERT_SUCCESS(conf.parse("include ./conf/include_test/include_2.conf;")); + + vector listens = conf.get_listens(); + EXPECT_EQ(1, (int)listens.size()); + EXPECT_STREQ("1935", listens.at(0).c_str()); + + EXPECT_FALSE(conf.get_log_tank_file()); + + EXPECT_TRUE(conf.get_http_stream_enabled()); + EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); + EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str()); + + EXPECT_TRUE(conf.get_hls_enabled("ossrs.net")); + EXPECT_STREQ("xxx", conf.get_hls_path("ossrs.net").c_str()); + EXPECT_STREQ("xxx1", conf.get_hls_m3u8_file("ossrs.net").c_str()); + EXPECT_STREQ("xxx2", conf.get_hls_ts_file("ossrs.net").c_str()); + EXPECT_EQ(10*SRS_UTIME_SECONDS, conf.get_hls_fragment("ossrs.net")); + EXPECT_EQ(60*SRS_UTIME_SECONDS, conf.get_hls_window("ossrs.net")); + } + + if (true) { + MockSrsConfig conf; + + conf.mock_include("./conf/include_test/include_3.conf", "hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}"); + conf.mock_include("./conf/include_test/include_4.conf", "listen 1935;max_connections 1000;daemon off;srs_log_tank console;include ./conf/include_test/include_5.conf;vhost ossrs.net {include ./conf/include_test/include_3.conf;}include ./conf/include_test/include_6.conf;"); + conf.mock_include("./conf/include_test/include_5.conf", "http_server {enabled on;listen xxx;dir xxx2;}"); + conf.mock_include("./conf/include_test/include_6.conf", "http_api {enabled on;listen xxx;}"); + + HELPER_ASSERT_SUCCESS(conf.parse("include ./conf/include_test/include_4.conf;")); + + vector listens = conf.get_listens(); + EXPECT_EQ(1, (int)listens.size()); + EXPECT_STREQ("1935", listens.at(0).c_str()); + + EXPECT_FALSE(conf.get_log_tank_file()); + + EXPECT_TRUE(conf.get_http_stream_enabled()); + EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); + EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str()); + + EXPECT_TRUE(conf.get_hls_enabled("ossrs.net")); + EXPECT_STREQ("xxx", conf.get_hls_path("ossrs.net").c_str()); + EXPECT_STREQ("xxx1", conf.get_hls_m3u8_file("ossrs.net").c_str()); + EXPECT_STREQ("xxx2", conf.get_hls_ts_file("ossrs.net").c_str()); + EXPECT_EQ(10*SRS_UTIME_SECONDS, conf.get_hls_fragment("ossrs.net")); + EXPECT_EQ(60*SRS_UTIME_SECONDS, conf.get_hls_window("ossrs.net")); + + EXPECT_TRUE(conf.get_http_api_enabled()); + EXPECT_STREQ("xxx", conf.get_http_api_listen().c_str()); + } + + if (true) { + MockSrsConfig conf; + + conf.mock_include("./conf/include_test/include_3.conf", "hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}"); + conf.mock_include("./conf/include_test/include_4.conf", "listen 1935;max_connections 1000;daemon off;srs_log_tank console;include ./conf/include_test/include_5.conf ./conf/include_test/include_6.conf;vhost ossrs.net {include ./conf/include_test/include_3.conf;}"); + conf.mock_include("./conf/include_test/include_5.conf", "http_server {enabled on;listen xxx;dir xxx2;}"); + conf.mock_include("./conf/include_test/include_6.conf", "http_api {enabled on;listen xxx;}"); + + HELPER_ASSERT_SUCCESS(conf.parse("include ./conf/include_test/include_4.conf;")); + + vector listens = conf.get_listens(); + EXPECT_EQ(1, (int)listens.size()); + EXPECT_STREQ("1935", listens.at(0).c_str()); + + EXPECT_FALSE(conf.get_log_tank_file()); + + EXPECT_TRUE(conf.get_http_stream_enabled()); + EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); + EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str()); + + EXPECT_TRUE(conf.get_http_api_enabled()); + EXPECT_STREQ("xxx", conf.get_http_api_listen().c_str()); + + EXPECT_TRUE(conf.get_hls_enabled("ossrs.net")); + EXPECT_STREQ("xxx", conf.get_hls_path("ossrs.net").c_str()); + EXPECT_STREQ("xxx1", conf.get_hls_m3u8_file("ossrs.net").c_str()); + EXPECT_STREQ("xxx2", conf.get_hls_ts_file("ossrs.net").c_str()); + EXPECT_EQ(10*SRS_UTIME_SECONDS, conf.get_hls_fragment("ossrs.net")); + EXPECT_EQ(60*SRS_UTIME_SECONDS, conf.get_hls_window("ossrs.net")); + } + + if (true) { + MockSrsConfig conf; + + conf.mock_include("./conf/include_test/include_3.conf", "hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}"); + conf.mock_include("./conf/include_test/include_4.conf", "listen 1935;max_connections 1000;daemon off;srs_log_tank console;include ./conf/include_test/include_5.conf ./conf/include_test/include_6.conf;vhost ossrs.net {include ./conf/include_test/include_3.conf ./conf/include_test/include_7.conf;}"); + conf.mock_include("./conf/include_test/include_5.conf", "http_server {enabled on;listen xxx;dir xxx2;}"); + conf.mock_include("./conf/include_test/include_6.conf", "http_api {enabled on;listen xxx;}"); + conf.mock_include("./conf/include_test/include_7.conf", "dash{enabled on;dash_fragment 10;dash_update_period 10;dash_timeshift 10;dash_path xxx;dash_mpd_file xxx2;}"); + + HELPER_ASSERT_SUCCESS(conf.parse("include ./conf/include_test/include_4.conf;")); + + vector listens = conf.get_listens(); + EXPECT_EQ(1, (int)listens.size()); + EXPECT_STREQ("1935", listens.at(0).c_str()); + + EXPECT_FALSE(conf.get_log_tank_file()); + + EXPECT_TRUE(conf.get_http_stream_enabled()); + EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); + EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str()); + + EXPECT_TRUE(conf.get_http_api_enabled()); + EXPECT_STREQ("xxx", conf.get_http_api_listen().c_str()); + + EXPECT_TRUE(conf.get_hls_enabled("ossrs.net")); + EXPECT_STREQ("xxx", conf.get_hls_path("ossrs.net").c_str()); + EXPECT_STREQ("xxx1", conf.get_hls_m3u8_file("ossrs.net").c_str()); + EXPECT_STREQ("xxx2", conf.get_hls_ts_file("ossrs.net").c_str()); + EXPECT_EQ(10*SRS_UTIME_SECONDS, conf.get_hls_fragment("ossrs.net")); + EXPECT_EQ(60*SRS_UTIME_SECONDS, conf.get_hls_window("ossrs.net")); + + EXPECT_EQ(10*SRS_UTIME_SECONDS, conf.get_dash_fragment("ossrs.net")); + EXPECT_EQ(10*SRS_UTIME_SECONDS, conf.get_dash_update_period("ossrs.net")); + EXPECT_EQ(10*SRS_UTIME_SECONDS, conf.get_dash_timeshift("ossrs.net")); + EXPECT_STREQ("xxx", conf.get_dash_path("ossrs.net").c_str()); + EXPECT_STREQ("xxx2", conf.get_dash_mpd_file("ossrs.net").c_str()); + } + + if (true) { + MockSrsConfig conf; + + HELPER_ASSERT_FAILED(conf.parse("include ./conf/include_test/include.conf;")); + } +} diff --git a/trunk/src/utest/srs_utest_config.hpp b/trunk/src/utest/srs_utest_config.hpp index a57581da3..fc31ba012 100644 --- a/trunk/src/utest/srs_utest_config.hpp +++ b/trunk/src/utest/srs_utest_config.hpp @@ -32,8 +32,13 @@ class MockSrsConfig : public SrsConfig public: MockSrsConfig(); virtual ~MockSrsConfig(); +private: + std::map included_files; public: virtual srs_error_t parse(std::string buf); + virtual srs_error_t mock_include(const std::string file_name, const std::string content); +protected: + virtual srs_error_t build_buffer(std::string src, srs_internal::SrsConfigBuffer** pbuffer); }; #endif