new obj.conf parser based on serverconfig parser

Mon, 07 Nov 2022 22:30:12 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Mon, 07 Nov 2022 22:30:12 +0100
changeset 419
f1d29785ad2d
parent 418
b7dcc9c4f270
child 420
0b42a35d6add

new obj.conf parser based on serverconfig parser

src/server/config/objconf.c file | annotate | diff | comparison | revisions
src/server/config/objconf.h file | annotate | diff | comparison | revisions
src/server/config/serverconfig.c file | annotate | diff | comparison | revisions
src/server/config/serverconfig.h file | annotate | diff | comparison | revisions
src/server/daemon/config.c file | annotate | diff | comparison | revisions
--- 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, &param_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;
 }

mercurial