Mon, 07 Nov 2022 22:30:12 +0100
new obj.conf parser based on serverconfig parser
--- a/src/server/config/objconf.c Mon Nov 07 17:59:44 2022 +0100 +++ b/src/server/config/objconf.c Mon Nov 07 22:30:12 2022 +0100 @@ -65,6 +65,152 @@ return conf; } +ObjectConfig2* objectconf_load(const char *file) { + CxMempool *mp = cxBasicMempoolCreate(512); + if(!mp) { + return NULL; + } + + // setup parser + ConfigParser2 parser; + memset(&parser, 0, sizeof(ConfigParser2)); + parser.mp = mp; + parser.validateDirective = objectconf_validate_directive; + parser.validateObjBegin = objectconf_validate_objbegin; + parser.validateObjEnd = objectconf_validate_objend; + parser.allow_hierarchy = 1; + + ConfigNode *obj_config = serverconfig_load_file(&parser, file); + if(!obj_config) { + cxMempoolDestroy(mp); + return NULL; + } + + ObjectConfig2 *conf = cxMalloc(mp->allocator, sizeof(ObjectConfig2)); + if(!conf) { + cxMempoolDestroy(mp); + return NULL; + } + + conf->mp = mp; + conf->root = obj_config; + + return conf; +} + +void objectconf_free(ObjectConfig2 *objconf) { + cxMempoolDestroy(objconf->mp); +} + +int objectconf_validate_directive(ConfigParser2 *parser, ConfigNode *node) { + // It is possible that this function is called for Objects + // if { is on the next line + // It may be possible to fix the parser to not do that, but it is fine + // to just deal with that here + + // ignore if node->name is an Object name like + // Object, Client, If, Else IfElse + const char *objnames[] = { "Object", "Client", "If", "ElseIf", "Else" }; + size_t typeindex; + if(!serverconfig_validate_directive_name(node, objnames, 5, &typeindex)) { + return 0; + } + + // now check directive type + const char *dirtypes[] = { "AuthTrans", "NameTrans", "PathCheck", "ObjectType", "Service", "AddLog", "Error" }; + if(serverconfig_validate_directive_name(node, dirtypes, 7, &typeindex)) { + return 1; + } + + // directives must have parameter names + ConfigParam *param_err; + if(serverconfig_check_param_names(node, ¶m_err)) { + return 1; + } + + // check if the fn parameter exists + cxstring fn = serverconfig_directive_get_arg(node, cx_str("fn")); + if(fn.length == 0) { + return 1; + } + + return 0; +} + +static int validate_else_node(ConfigNode *node) { + // previous node must be "If" or "ElseIf" + ConfigNode *prev = serverconfig_previous_dir_or_obj(node); + const char *allowed_prev_types[] = { "If", "ElseIf" }; + size_t typeindex; + if(serverconfig_validate_directive_name(prev, allowed_prev_types, 2, &typeindex)) { + return 1; // prevous node (not counting space, comments) is not "If" or "Else" + } + + // "Else" must have no args + return node->args ? 1 : 0; +} + +static int validate_elseif_node(ConfigNode *node) { + // the previous node must be "If" or "IfElse" + // in case if "IfElse", we also have to check all previous nodes, until + // we find the starting "If" node + const char *allowed_prev_types[] = { "If", "ElseIf" }; + size_t typeindex; + ConfigNode *prev = serverconfig_previous_dir_or_obj(node); + while(prev) { + if(serverconfig_validate_directive_name(prev, allowed_prev_types, 2, &typeindex)) { + return 1; // previous node was not "If" or "ElseIf" + } + // we found a valid "If" node, the whole construct is valid + if(typeindex == 0) { + return 0; + } + + prev = serverconfig_previous_dir_or_obj(node); + } + // at this point we found only "IfElse" nodes, but no "If" node + return 1; +} + +int objectconf_validate_objbegin(ConfigParser2 *parser, ConfigNode *node) { + // get object type + const char *objnames[] = { "Object", "Client", "If", "ElseIf", "Else" }; + size_t typeindex; + if(serverconfig_validate_directive_name(node, objnames, 5, &typeindex)) { + return 1; // unknown object type + } + + // some special requirements for "Else" node + if(typeindex == 4) { + // type is "Else" + return validate_else_node(node); + } + + // check "ElseIf" was used after "If" or "IfElse" + if(typeindex == 3 && validate_elseif_node(node)) { + return 1; + } + + // only Object must have a name/value arg, other types can have more + // complex parameters + if(typeindex == 0) { + // type is "Object" + ConfigParam *err; + if(serverconfig_check_param_names(node, &err)) { + return 1; + } + return 0; + } + + // all remaining object types must have arguments, but don't check more + return node->args ? 0 : 1; +} + +int objectconf_validate_objend(ConfigParser2 *parser, ConfigNode *node) { + return 0; +} + + void free_object_config(ObjectConfig *conf) { // free other lists if(conf->levels) {
--- a/src/server/config/objconf.h Mon Nov 07 17:59:44 2022 +0100 +++ b/src/server/config/objconf.h Mon Nov 07 22:30:12 2022 +0100 @@ -31,6 +31,9 @@ #include "conf.h" + +#include "serverconfig.h" + #ifdef __cplusplus extern "C" { #endif @@ -73,6 +76,24 @@ } ObjectConfig; +// TODO: rename to ObjectConfig after old ObjectConfig is removed +typedef struct ObjectConfig2 { + CxMempool *mp; + ConfigNode *root; +} ObjectConfig2; + +ObjectConfig2* objectconf_load(const char *file); + +void objectconf_free(ObjectConfig2 *objconf); + +int objectconf_validate_directive(ConfigParser2 *parser, ConfigNode *node); + +int objectconf_validate_objbegin(ConfigParser2 *parser, ConfigNode *node); + +int objectconf_validate_objend(ConfigParser2 *parser, ConfigNode *node); + + + ObjectConfig *load_object_config(char *file); void free_object_config(ObjectConfig *conf);
--- a/src/server/config/serverconfig.c Mon Nov 07 17:59:44 2022 +0100 +++ b/src/server/config/serverconfig.c Mon Nov 07 22:30:12 2022 +0100 @@ -294,6 +294,12 @@ obj->text_begin = new_textbegin; } else { // normal line containing a directive, space or comment + + // add it to parent node + current->text_begin = line_cp; + CFG_NODE_ADD(&parent->children_begin, &parent->children_end, current); + + // validate after CFG_NODE_ADD, because now current->prev is set if(current->type == CONFIG_NODE_DIRECTIVE && parser->validateDirective && parser->validateDirective(parser, current)) @@ -301,9 +307,6 @@ err = 1; break; } - // add it to parent node - current->text_begin = line_cp; - CFG_NODE_ADD(&parent->children_begin, &parent->children_end, current); } // obj points to the previous node that started as a directive @@ -478,3 +481,26 @@ } return 0; } + +ConfigNode* serverconfig_previous_dir_or_obj(ConfigNode *node) { + node = node->prev; + while(node) { + if(node->type == CONFIG_NODE_DIRECTIVE || node->type == CONFIG_NODE_OBJECT) { + return node; + } + node = node->prev; + } + return NULL; +} + +size_t serverconfig_children_count(ConfigNode *node, ConfigNodeType type) { + size_t count = 0; + node = node->children_begin; + while(node) { + if(node->type == type) { + count++; + } + node = node->next; + } + return count; +}
--- a/src/server/config/serverconfig.h Mon Nov 07 17:59:44 2022 +0100 +++ b/src/server/config/serverconfig.h Mon Nov 07 22:30:12 2022 +0100 @@ -197,6 +197,16 @@ */ int serverconfig_check_param_names(ConfigNode *directive, ConfigParam **err); +/* + * Returns the previous node that is a directive or object + */ +ConfigNode* serverconfig_previous_dir_or_obj(ConfigNode *node); + +/* + * Count the direct children that have the specified type + */ +size_t serverconfig_children_count(ConfigNode *node, ConfigNodeType type); + #ifdef __cplusplus } #endif
--- a/src/server/daemon/config.c Mon Nov 07 17:59:44 2022 +0100 +++ b/src/server/daemon/config.c Mon Nov 07 22:30:12 2022 +0100 @@ -75,7 +75,6 @@ log_ereport(LOG_FAILURE, "Cannot load init.conf"); return 1; } - CxAllocator *mp = cfg->mp; init_pool = pool_create(); ConfigNode *dir = cfg->root->children_begin; @@ -838,78 +837,82 @@ return ret; } -static int convert_objconf(ServerConfiguration *scfg, ObjectConfig *cfg, HTTPObjectConfig *conf, cxmutstr file) { +static int convert_objconf(ServerConfiguration *scfg, ObjectConfig2 *cfg, HTTPObjectConfig *conf, cxmutstr file) { pool_handle_t *pool = conf->pool; - CxList *objlist = cfg->objects; - CxIterator iter = cxListIterator(objlist, 0); int i = 0; - cx_foreach(ConfigObject*, cob, iter) { + for(ConfigNode *objnode=cfg->root->children_begin;objnode;objnode=objnode->next) { + if(objnode->type != CONFIG_NODE_OBJECT) { + continue; + } + // get name and ppath + cxmutstr cfg_name = cfg_param_get(objnode->args, cx_str("name")); + cxmutstr cfg_ppath = cfg_param_get(objnode->args, cx_str("ppath")); + char *name = NULL; char *ppath = NULL; - if(cob->name.length > 0) { - name = cx_strdup_pool(pool, cob->name).ptr; + + if(cfg_name.length > 0) { + name = cx_strdup_pool(pool, cfg_name).ptr; if(!name) return -1; } - if(cob->ppath.length > 0) { - ppath = cx_strdup_pool(pool, cob->ppath).ptr; + if(cfg_ppath.length > 0) { + ppath = cx_strdup_pool(pool, cfg_ppath).ptr; if(!ppath) return -1; } - + // create and add object httpd_object *obj = object_new(pool, name); if(!obj) return -1; obj->path = NULL; - + conf->objects[i] = obj; - + + // TODO: this doesn't work with If/Else... nodes + // and needs to be rewritten // add directives - for(int j=0;j<NUM_NSAPI_TYPES-1;j++) { - ConfigDirectiveList *dirs = cob->directives[j]; - while(dirs != NULL) { - ConfigDirective *cfgdir = dirs->directive; - - directive *d = pool_malloc(pool, sizeof(directive)); - if(!d) return -1; - if(cfgdir->condition) { - cxmutstr expr = cfgdir->condition->param_str; - d->cond = condition_from_str(pool, expr.ptr, expr.length); - } else { - d->cond = NULL; - } - d->param = pblock_create_pool(pool, 8); + for(ConfigNode *dir=objnode->children_begin;dir;dir=dir->next) { + if(dir->type != CONFIG_NODE_DIRECTIVE) { + continue; + } + + directive *d = pool_malloc(pool, sizeof(directive)); + if(!d) return -1; + d->param = pblock_create_pool(pool, 8); - // add params - ConfigParam *param = cfg_param_list(cfgdir->value, scfg->a); - while(param != NULL) { - pblock_nvlinsert( - param->name.ptr, - param->name.length, - param->value.ptr, - param->value.length, - d->param); - param = param->next; - } + // add params + ConfigParam *param = dir->args; + while(param != NULL) { + pblock_nvlinsert( + param->name.ptr, + param->name.length, + param->value.ptr, + param->value.length, + d->param); + param = param->next; + } - // get function - char *func_name = pblock_findval("fn", d->param); - if(!func_name) { - log_ereport(LOG_MISCONFIG, "%s: Missing fn parameter", file.ptr); - return -1; - } - d->func = get_function(func_name); - if(!d->func) { - log_ereport(LOG_MISCONFIG, "func %s not found", func_name); - return -1; - } + // get function + char *func_name = pblock_findval("fn", d->param); + if(!func_name) { + log_ereport(LOG_MISCONFIG, "%s: Missing fn parameter", file.ptr); + return -1; + } + d->func = get_function(func_name); + if(!d->func) { + log_ereport(LOG_MISCONFIG, "func %s not found", func_name); + return -1; + } - dirs = dirs->next; - - // add function to dtable - object_add_directive(obj, d, cfgdir->type_num); + // add function to dtable + int dir_type = cfg_get_directive_type_num(cx_strcast(dir->name)); + if(dir_type < 0) { + log_ereport(LOG_MISCONFIG, "unknown directive type %s", dir->name); } + object_add_directive(obj, d, dir_type); } + // next i++; @@ -932,7 +935,7 @@ conf->pool = pool; // load obj config file - ObjectConfig *cfg = load_object_config(file.ptr); + ObjectConfig2 *cfg = objectconf_load(file.ptr); if(!cfg) { return NULL; } @@ -940,7 +943,7 @@ // convert ObjectConfig to HTTPObjectConfig // add objects - conf->nobj = cfg->objects->size; + conf->nobj = serverconfig_children_count(cfg->root, CONFIG_NODE_OBJECT); conf->objects = pool_calloc(pool, conf->nobj, sizeof(httpd_object*)); if(conf->objects) { ret = convert_objconf(scfg, cfg, conf, file); @@ -948,7 +951,7 @@ ret = -1; } - free_object_config(cfg); + objectconf_free(cfg); return !ret ? conf : NULL; }