diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 04d98a4d46..a9aa056356 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -844,7 +844,7 @@ bool SrsConfDirective::is_stream_caster() srs_error_t SrsConfDirective::parse(SrsConfigBuffer* buffer, SrsConfig* conf) { - return parse_conf(buffer, parse_file, conf); + return parse_conf(buffer, SrsDirectiveContextFile, conf); } srs_error_t SrsConfDirective::persistence(SrsFileWriter* writer, int level) @@ -971,80 +971,30 @@ SrsJsonAny* SrsConfDirective::dumps_arg0_to_boolean() // LCOV_EXCL_STOP // see: ngx_conf_parse -srs_error_t SrsConfDirective::parse_conf(SrsConfigBuffer* buffer, SrsDirectiveType type, SrsConfig* conf) +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. - if (args.at(0) == "include") { - if (args.size() < 2) { - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: include is empty directive", buffer->line); - } - - for (int i = 1; i < (int)args.size(); i++) { - std::string file = args.at(i); - - srs_freep(err); - if (file.empty()) { - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "empty include config"); - } - - srs_trace("config parse include %s", file.c_str()); - if (type != parse_block) { - if ((err = conf->parse_include_file(file.c_str())) != srs_success) { - return srs_error_wrap(err, "parse file"); - } - } else { - SrsConfigBuffer* config_buffer = conf->get_buffer_from_include_file(file.c_str()); - SrsAutoFree(SrsConfigBuffer, config_buffer); - - if(config_buffer == NULL) { - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "empty include buffer, file=%s", file.c_str()); - } else { - if ((err = parse_conf(config_buffer, parse_file, conf)) != srs_success) { - return srs_error_wrap(err, "parse include buffer"); - } - } - } - } - } else { + // Build normal directive which is not "include". + if (args.at(0) != "include") { SrsConfDirective* directive = new SrsConfDirective(); directive->conf_line = line_start; @@ -1054,21 +1004,45 @@ srs_error_t SrsConfDirective::parse_conf(SrsConfigBuffer* buffer, SrsDirectiveTy 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, conf)) != srs_success) { + 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; @@ -1090,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++; @@ -1112,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); } @@ -1131,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; @@ -1196,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; } } } @@ -2439,53 +2421,33 @@ 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::parse_include_file(const char *filename) -{ - srs_error_t err = srs_success; - - std::string file = filename; - - SrsConfigBuffer buffer; - - if ((err = buffer.fullfill(file.c_str())) != srs_success) { - return srs_error_wrap(err, "buffer fullfil"); - } - - // Parse root tree from buffer. - if ((err = root->parse(&buffer, this)) != srs_success) { - return srs_error_wrap(err, "parse include buffer"); - } - - return err; -} - -SrsConfigBuffer* SrsConfig::get_buffer_from_include_file(const char* filename) +srs_error_t SrsConfig::build_buffer(string src, SrsConfigBuffer** pbuffer) { srs_error_t err = srs_success; SrsConfigBuffer* buffer = new SrsConfigBuffer(); - if ((err = buffer->fullfill(filename)) != srs_success) { + if ((err = buffer->fullfill(src.c_str())) != srs_success) { srs_freep(buffer); - - return NULL; + return srs_error_wrap(err, "read from src %s", src.c_str()); } - return buffer; + *pbuffer = buffer; + return err; } // LCOV_EXCL_STOP diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index f4abfaee46..7623dc04db 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -223,24 +223,36 @@ class SrsConfDirective 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, SrsConfig* conf); + 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 @@ class SrsConfDirective // 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,10 +369,10 @@ class SrsConfig public: // Parse the config file, which is specified by cli. virtual srs_error_t parse_file(const char* filename); - // Parse the include config file. - virtual srs_error_t parse_include_file(const char* filename); - // Get buffer from include config file. - virtual srs_internal::SrsConfigBuffer* get_buffer_from_include_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 722ca60b44..c07bb84c80 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 540572874b..44421cd42b 100644 --- a/trunk/src/utest/srs_utest_config.cpp +++ b/trunk/src/utest/srs_utest_config.cpp @@ -50,37 +50,6 @@ MockSrsConfig::~MockSrsConfig() { } -SrsConfDirective* MockSrsConfig::get_mock_directive(const string file_name) -{ - SrsConfDirective* mock_directive = NULL; - - std::vector::iterator it; - for (it = mock_directives.begin(); it != mock_directives.end(); ++it) { - SrsConfDirective* directive = *it; - if (directive->name == file_name) { - mock_directive = directive; - } - } - - return mock_directive; -} - -MockSrsConfigBuffer* MockSrsConfig::get_buffer_from_include_file(const char* filename) -{ - MockSrsConfigBuffer* buffer = NULL; - - std::string file = filename; - SrsConfDirective* mock_directive = get_mock_directive(file); - - if(!mock_directive) { - return NULL; - } else { - buffer = new MockSrsConfigBuffer(mock_directive->arg0()); - } - - return buffer; -} - srs_error_t MockSrsConfig::parse(string buf) { srs_error_t err = srs_success; @@ -102,44 +71,21 @@ srs_error_t MockSrsConfig::parse(string buf) return err; } -srs_error_t MockSrsConfig::parse_include_file(const char *filename) +srs_error_t MockSrsConfig::mock_include(const string file_name, const string content) { srs_error_t err = srs_success; - std::string file = filename; - - if (file.empty()) { - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "empty include config"); - } - - SrsConfDirective* mock_directive = get_mock_directive(file); - - if(!mock_directive) { - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "empty include config"); - } else { - MockSrsConfigBuffer buffer(mock_directive->arg0()); - - // Parse root tree from buffer. - if ((err = root->parse(&buffer, this)) != srs_success) { - return srs_error_wrap(err, "parse include buffer"); - } - } + included_files[file_name] = content; return err; } -srs_error_t MockSrsConfig::mock_include(const string file_name, const string content) +srs_error_t MockSrsConfig::build_buffer(std::string src, srs_internal::SrsConfigBuffer** pbuffer) { srs_error_t err = srs_success; - SrsConfDirective* mock_directive = get_mock_directive(file_name); - - if (!mock_directive) { - mock_directive = new SrsConfDirective(); - mock_directive->name = file_name; - mock_directive->args.push_back(content); - mock_directives.push_back(mock_directive); - } + string content = included_files[src]; + *pbuffer = new MockSrsConfigBuffer(content); return err; } @@ -376,6 +322,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; diff --git a/trunk/src/utest/srs_utest_config.hpp b/trunk/src/utest/srs_utest_config.hpp index 47940dec40..fc31ba0128 100644 --- a/trunk/src/utest/srs_utest_config.hpp +++ b/trunk/src/utest/srs_utest_config.hpp @@ -33,14 +33,12 @@ class MockSrsConfig : public SrsConfig MockSrsConfig(); virtual ~MockSrsConfig(); private: - std::vector mock_directives; + std::map included_files; public: - SrsConfDirective* get_mock_directive(const std::string file_name); -public: - virtual MockSrsConfigBuffer* get_buffer_from_include_file(const char* filename); virtual srs_error_t parse(std::string buf); - virtual srs_error_t parse_include_file(const char* filename); 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