Sun, 15 May 2022 08:56:00 +0200
make sure the http stream is finished if headers are sent
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2020 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "serverconfig.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <ucx/buffer.h> #include <ucx/utils.h> ServerConfig* serverconfig_load(const char *file) { FILE *in = fopen(file, "r"); if(in == NULL) { return NULL; } UcxBuffer *buf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); if(!buf) { fclose(in); return NULL; } ucx_stream_copy(in, buf, (read_func)fread, (write_func)ucx_buffer_write); fclose(in); ServerConfig *scfg = serverconfig_parse(scstrn(buf->space, buf->size)); ucx_buffer_free(buf); return scfg; } static CFGToken get_next_token(scstr_t content, int *pos) { CFGToken token = { {NULL, 0}, CFG_NO_TOKEN }; CFGTokenType type = CFG_TOKEN; int start = *pos; int token_begin = -1; int token_end = content.length-1; int quote = 0; int comment = 0; int i; char prev = 0; for(i=start;i<content.length;i++) { char c = content.ptr[i]; if(c == '\n') { if(quote) { *pos = i; return token; // error } else if(start == i) { // single newline char token type = CFG_TOKEN_NEWLINE; token_begin = i; token_end = i+1; break; } token_end = i; if(token_begin < 0) { // only space/comment token token_begin = start; type = comment ? CFG_TOKEN_COMMENT : CFG_TOKEN_SPACE; } // make sure next run will return current newline char as token i--; break; } else if(quote) { if(c == '"' && prev != '\\') { quote = 0; } } else if(comment) { // ignore if(c == '\n') { comment = 0; } } else if(c == '#') { comment = 1; } else if(isspace(c)) { if(token_begin >= 0) { token_end = i; break; } } else if(c == '"') { quote = 1; if(token_begin < 0) { token_begin = i; } } else if(token_begin < 0) { token_begin = i; } prev = c; } *pos = i + 1; if(token_begin < 0) { return token; // error } token.type = type; token.content = scstrsubsl(content, token_begin, token_end - token_begin); return token; } /* static void test_print_config(ConfigNode *parent) { UCX_FOREACH(elm, parent->children) { ConfigNode *node = elm->data; if(node->type == CONFIG_NODE_SPACE) { printf("sp: %s", node->text_begin.ptr); } else if(node->type == CONFIG_NODE_COMMENT) { printf("cm: %s", node->text_begin.ptr); } else if(node->type == CONFIG_NODE_OBJECT) { printf("o{: %s : %s", node->name.ptr, node->text_begin.ptr); test_print_config(node); printf("o}: %s", node->text_end.ptr); } else if(node->type == CONFIG_NODE_DIRECTIVE) { printf("di: %s", node->text_begin.ptr); } else { printf("fk: %s", node->text_begin.ptr); } } } */ ServerConfig* serverconfig_parse(scstr_t content) { UcxMempool *mp = ucx_mempool_new(512); if(!mp) return NULL; UcxAllocator *a = mp->allocator; ServerConfig *config = ucx_mempool_malloc(mp, sizeof(ServerConfig)); if(!config) { ucx_mempool_destroy(mp); return NULL; } config->mp = mp; // PARSE: // first non space/comment token is directive/object name // following tokens are arguments // newline starts new directive // '{' converts directive to object and following directives will // be placed into the object int pos = 0; // needed for tokenizer CFGToken token; ConfigNode *root_obj = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode)); root_obj->type = CONFIG_NODE_OBJECT; UcxList *node_stack = ucx_list_prepend(NULL, root_obj); ConfigNode *current = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode)); current->type = CONFIG_NODE_SPACE; ConfigNode *obj = NULL; int obj_closed = 0; int text_start = 0; int err = 0; while((token = get_next_token(content, &pos)).type != CFG_NO_TOKEN) { //printf("%s [%.*s]\n", token_type_str(token.type), (int)token.content.length, token.content.ptr); switch(token.type) { case CFG_NO_TOKEN: break; case CFG_TOKEN_COMMENT: { if(current->type == CONFIG_NODE_SPACE) { current->type = CONFIG_NODE_COMMENT; } break; } case CFG_TOKEN_SPACE: break; case CFG_TOKEN_NEWLINE: { scstr_t line = scstrsubsl(content, text_start, pos - text_start); text_start = pos; sstr_t line_cp = sstrdup_a(a, line); ConfigNode *parent = node_stack->data; if(current->type == CONFIG_NODE_CLOSE_OBJECT) { parent->text_end = line_cp; node_stack = ucx_list_remove_a(a, node_stack, node_stack); } else if(current->type == CONFIG_NODE_OPEN_OBJECT) { sstr_t new_textbegin = sstrcat_a(a, 2, obj->text_begin, line_cp); alfree(a, obj->text_begin.ptr); alfree(a, line_cp.ptr); obj->text_begin = new_textbegin; } else { current->text_begin = line_cp; ConfigNode *parent = node_stack->data; parent->children = ucx_list_append_a(a, parent->children, current); } if(obj && obj->type == CONFIG_NODE_OBJECT) { node_stack = ucx_list_prepend_a(a, node_stack, obj); obj = NULL; } current = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode)); current->type = CONFIG_NODE_SPACE; obj_closed = 0; break; } case CFG_TOKEN: { if(!sstrcmp(token.content, S("{"))) { if(!obj) { err = 1; break; } obj->type = CONFIG_NODE_OBJECT; if(current != obj) { current->type = CONFIG_NODE_OPEN_OBJECT; } } else if(!sstrcmp(token.content, S("}"))) { obj_closed = 1; // force newline before next directive obj = NULL; current->type = CONFIG_NODE_CLOSE_OBJECT; } else { if(obj_closed) { err = 1; break; } if(!current->name.ptr) { current->name = sstrdup_a(a, token.content); current->type = CONFIG_NODE_DIRECTIVE; obj = current; } else { ConfigArg *arg = ucx_mempool_calloc(mp, 1, sizeof(ConfigArg)); // TODO: add support for key/value arg->value = sstrdup_a(a, token.content); current->args = ucx_list_append_a(a, current->args, arg); } } break; } } if(err) { break; } } if(pos < content.length || err) { // content not fully parsed because of an error ucx_mempool_destroy(mp); return NULL; } //test_print_config(&root_obj); config->root = root_obj; config->tab = sstrdup_a(a, SC("\t")); return config; } void serverconfig_free(ServerConfig *cfg) { ucx_mempool_destroy(cfg->mp); } ConfigNode* serverconfig_get_node(ConfigNode *parent, ConfigNodeType type, scstr_t name) { UCX_FOREACH(elm, parent->children) { ConfigNode *node = elm->data; if(node->type == type && !sstrcmp(node->name, name)) { return node; } } return NULL; } UcxList* serverconfig_get_node_list(ConfigNode *parent, ConfigNodeType type, scstr_t name) { UcxList *nodes = NULL; UCX_FOREACH(elm, parent->children) { ConfigNode *node = elm->data; if(node->type == type && !sstrcmp(node->name, name)) { nodes = ucx_list_append(nodes, node); } } return nodes; } scstr_t serverconfig_directive_value(ConfigNode *obj, scstr_t name) { ConfigNode *node = serverconfig_get_node(obj, CONFIG_NODE_DIRECTIVE, name); if(node && ucx_list_size(node->args) == 1) { ConfigArg *arg = node->args->data; return SCSTR(arg->value); } return scstrn(NULL, 0); } sstr_t serverconfig_arg_name_value(UcxAllocator *a, scstr_t str, scstr_t *name) { int valstart = 0; for(int i=0;i<str.length;i++) { if(str.ptr[i] == '=') { if(name) { name->ptr = str.ptr; name->length = i; } valstart = i + 1; break; } } sstr_t ret; return ret; }