mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
Support include directive for config file (#2878)
* Support include import configuration * Remove support for regular rules * Remove support for regular rules * Fix configuration file parsing bug * Added utest tests for include functionality * Added utest tests for include functionality * Modify the UTest function * optimized code * Config: Refine parse error with state * Config: Reorder functions * Config: Rename parsing type to context * Config: Refine args for include * Config: Add utests for include * Config: Refine code, parsing recursively. * Config: Change the mock from file to buffer * Config: Mock buffer in config * Config: Refine code * Add utests for include * Added utest for include Co-authored-by: pengfei.ma <pengfei.ma@ngaa.com.cn> Co-authored-by: winlin <winlin@vip.126.com>
This commit is contained in:
parent
5e78c1fe88
commit
fde44885d9
5 changed files with 389 additions and 74 deletions
|
|
@ -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<string> 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<string> 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<string>& args, int& line_start)
|
||||
srs_error_t SrsConfDirective::read_token(SrsConfigBuffer* buffer, vector<string>& 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<string>
|
|||
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<string>
|
|||
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<string>
|
|||
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<string>
|
|||
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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<std::string>& args, int& line_start);
|
||||
virtual srs_error_t read_token(srs_internal::SrsConfigBuffer* buffer, std::vector<std::string>& 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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue