[go: nahoru, domu]

Skip to content

Commit

Permalink
Merge pull request h2o#1606 from h2o/kazuho/parse-mapping
Browse files Browse the repository at this point in the history
refactor YOML attribute parsing
  • Loading branch information
kazuho committed Jan 18, 2018
2 parents 1f294b1 + f331883 commit 74a13a2
Show file tree
Hide file tree
Showing 10 changed files with 501 additions and 663 deletions.
14 changes: 14 additions & 0 deletions include/h2o/configurator.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,20 @@ int h2o_configurator_scanf(h2o_configurator_command_t *cmd, yoml_t *node, const
* @return index of the matched string within the given list, or -1 if none of them matched
*/
ssize_t h2o_configurator_get_one_of(h2o_configurator_command_t *cmd, yoml_t *node, const char *candidates);
/**
* extracts values (required and optional) from a mapping by their keys, or prints an error upon failure
* @param configurator configurator
* @param node the mapping to parse
* @param keys_required comma-separated list of required keys (or NULL)
* @param keys_optional comma-separated list of optional keys (or NULL)
* @param ... pointers to `yoml_t **` for receiving the results; they should appear in the order they appear in the key names
* @return 0 if successful, -1 if not
*/
#define h2o_configurator_parse_mapping(cmd, node, keys_required, keys_optional, ...) \
h2o_configurator__do_parse_mapping((cmd), (node), (keys_required), (keys_optional), (yoml_t * **[]){__VA_ARGS__}, \
sizeof((yoml_t ***[]){__VA_ARGS__}) / sizeof(yoml_t ***))
int h2o_configurator__do_parse_mapping(h2o_configurator_command_t *cmd, yoml_t *node, const char *keys_required,
const char *keys_optional, yoml_t ****values, size_t num_values);
/**
* returns the absolute paths of supplementary commands
*/
Expand Down
188 changes: 153 additions & 35 deletions lib/core/configurator.c
Original file line number Diff line number Diff line change
Expand Up @@ -522,24 +522,21 @@ static int on_config_http2_casper(h2o_configurator_command_t *cmd, h2o_configura
/* set to default */
self->vars->http2.casper = defaults;
/* override the attributes defined */
yoml_t *t;
if ((t = yoml_get(node, "capacity-bits")) != NULL) {
if (!(t->type == YOML_TYPE_SCALAR && sscanf(t->data.scalar, "%u", &self->vars->http2.casper.capacity_bits) == 1 &&
yoml_t **capacity_bits, **tracking_types;
if (h2o_configurator_parse_mapping(cmd, node, NULL, "capacity-bits:s,tracking-types:*", &capacity_bits, &tracking_types) !=
0)
return -1;
if (capacity_bits != NULL) {
if (!(sscanf((*capacity_bits)->data.scalar, "%u", &self->vars->http2.casper.capacity_bits) == 1 &&
self->vars->http2.casper.capacity_bits < 16)) {
h2o_configurator_errprintf(cmd, t, "value of `capacity-bits` must be an integer between 0 to 15");
return -1;
}
}
if ((t = yoml_get(node, "tracking-types")) != NULL) {
if (t->type == YOML_TYPE_SCALAR && strcasecmp(t->data.scalar, "blocking-assets") == 0) {
self->vars->http2.casper.track_all_types = 0;
} else if (t->type == YOML_TYPE_SCALAR && strcasecmp(t->data.scalar, "all") == 0) {
self->vars->http2.casper.track_all_types = 1;
} else {
h2o_configurator_errprintf(cmd, t, "value of `tracking-types` must be either of: `blocking-assets` or `all`");
h2o_configurator_errprintf(cmd, *capacity_bits, "value of `capacity-bits` must be an integer between 0 to 15");
return -1;
}
}
if (tracking_types != NULL &&
(self->vars->http2.casper.track_all_types =
(int)h2o_configurator_get_one_of(cmd, *tracking_types, "blocking-assets,all")) == -1)
return -1;
} break;
default:
h2o_configurator_errprintf(cmd, node, "value must be `OFF`,`ON` or a mapping containing the necessary attributes");
Expand Down Expand Up @@ -605,39 +602,38 @@ static int set_mimetypes(h2o_configurator_command_t *cmd, h2o_mimemap_t *mimemap
}
break;
case YOML_TYPE_MAPPING: {
yoml_t *t;
yoml_t **is_compressible, **priority, **extensions;
h2o_mime_attributes_t attr;
h2o_mimemap_get_default_attributes(key->data.scalar, &attr);
if ((t = yoml_get(value, "is_compressible")) != NULL) {
if (t->type == YOML_TYPE_SCALAR && strcasecmp(t->data.scalar, "YES") == 0) {
if (h2o_configurator_parse_mapping(cmd, value, "extensions:a", "is_compressible:*,priority:*", &extensions,
&is_compressible, &priority) != 0)
return -1;
if (is_compressible != NULL) {
switch (h2o_configurator_get_one_of(cmd, *is_compressible, "YES,NO")) {
case 0:
attr.is_compressible = 1;
} else if (t->type == YOML_TYPE_SCALAR && strcasecmp(t->data.scalar, "NO") == 0) {
break;
case 1:
attr.is_compressible = 0;
} else {
h2o_configurator_errprintf(cmd, t, "`is_compressible` attribute must be either of: `YES` or `NO`");
break;
default:
return -1;
}
}
if ((t = yoml_get(value, "priority")) != NULL) {
if (t->type == YOML_TYPE_SCALAR && strcasecmp(t->data.scalar, "normal") == 0) {
if (priority != NULL) {
switch (h2o_configurator_get_one_of(cmd, *priority, "normal,highest")) {
case 0:
attr.priority = H2O_MIME_ATTRIBUTE_PRIORITY_NORMAL;
} else if (t->type == YOML_TYPE_SCALAR && strcasecmp(t->data.scalar, "highest") == 0) {
break;
case 1:
attr.priority = H2O_MIME_ATTRIBUTE_PRIORITY_HIGHEST;
} else {
h2o_configurator_errprintf(cmd, t, "`priority` attribute must be either of: `normal` or `highest`");
break;
default:
return -1;
}
}
if ((t = yoml_get(value, "extensions")) == NULL) {
h2o_configurator_errprintf(cmd, value, "cannot find mandatory attribute `extensions`");
return -1;
}
if (t->type != YOML_TYPE_SEQUENCE) {
h2o_configurator_errprintf(cmd, t, "`extensions` attribute must be a sequence of scalars");
return -1;
}
for (j = 0; j != t->data.sequence.size; ++j) {
yoml_t *ext_node = t->data.sequence.elements[j];
for (j = 0; j != (*extensions)->data.sequence.size; ++j) {
yoml_t *ext_node = (*extensions)->data.sequence.elements[j];
if (assert_is_extension(cmd, ext_node) != 0)
return -1;
h2o_mimemap_define_mimetype(mimemap, ext_node->data.scalar + 1, key->data.scalar, &attr);
Expand Down Expand Up @@ -1115,6 +1111,128 @@ ssize_t h2o_configurator_get_one_of(h2o_configurator_command_t *cmd, yoml_t *nod
return -1;
}

static const char *get_next_key(const char *start, h2o_iovec_t *output, unsigned *type_mask)
{
const char *p = strchr(start, ':');
if (p == NULL)
goto Error;

/* set output */
*output = h2o_iovec_init(start, p - start);

/* parse attributes */
*type_mask = 0;
for (++p; *p != '\0'; ++p) {
switch (*p) {
case ',':
return p + 1;
case 's':
*type_mask |= 1u << YOML_TYPE_SCALAR;
break;
case 'a':
*type_mask |= 1u << YOML_TYPE_SEQUENCE;
break;
case 'm':
*type_mask |= 1u << YOML_TYPE_MAPPING;
break;
case '*':
*type_mask |= (1u << YOML_TYPE_SCALAR) | (1u << YOML_TYPE_SEQUENCE) | (1u << YOML_TYPE_MAPPING);
break;
default:
goto Error;
}
}

return NULL;

Error:
fprintf(stderr, "%s: detected invalid or missing type specifier; input is: %s\n", __FUNCTION__, start);
abort();
}

int h2o_configurator__do_parse_mapping(h2o_configurator_command_t *cmd, yoml_t *node, const char *keys_required,
const char *keys_optional, yoml_t ****values, size_t num_values)
{
struct {
h2o_iovec_t key;
int is_required;
unsigned type_mask;
} *keys = alloca(sizeof(keys[0]) * num_values);
size_t i, j;

assert(node->type == YOML_TYPE_MAPPING);

/* parse keys */
i = 0;
if (keys_required != NULL) {
const char *p = keys_required;
for (; p != NULL; ++i) {
assert(i < num_values);
p = get_next_key(p, &keys[i].key, &keys[i].type_mask);
keys[i].is_required = 1;
}
}
if (keys_optional != NULL) {
const char *p = keys_optional;
for (; p != NULL; ++i) {
assert(i < num_values);
p = get_next_key(p, &keys[i].key, &keys[i].type_mask);
keys[i].is_required = 0;
}
}
assert(i == num_values);

/* clear the output */
for (i = 0; i != num_values; ++i)
*values[i] = NULL;

/* extract the attributes */
for (i = 0; i != node->data.mapping.size; ++i) {
yoml_mapping_element_t *element = node->data.mapping.elements + i;
if (element->key->type != YOML_TYPE_SCALAR) {
h2o_configurator_errprintf(cmd, element->key, "key must be a scalar");
return -1;
}
size_t element_key_len = strlen(element->key->data.scalar);
for (j = 0; j != num_values; ++j)
if (keys[j].key.len == element_key_len &&
strncasecmp(keys[j].key.base, element->key->data.scalar, element_key_len) == 0)
goto Found;
/* not found */
h2o_configurator_errprintf(cmd, element->key, "unexpected key:%s", element->key->data.scalar);
return -1;
Found:
if (*values[j] != NULL) {
h2o_configurator_errprintf(cmd, element->key, "duplicate key found");
return -1;
}
if ((keys[j].type_mask & (1u << element->value->type)) == 0) {
char permitted_types[32] = "";
if ((keys[j].type_mask & (1u << YOML_TYPE_SCALAR)) != 0)
strcat(permitted_types, " or a scalar");
if ((keys[j].type_mask & (1u << YOML_TYPE_SEQUENCE)) != 0)
strcat(permitted_types, " or a sequence");
if ((keys[j].type_mask & (1u << YOML_TYPE_MAPPING)) != 0)
strcat(permitted_types, " or a mapping");
assert(strlen(permitted_types) != 0);
h2o_configurator_errprintf(cmd, element->value, "attribute `%s` must be %s", element->key->data.scalar,
permitted_types + 4);
return -1;
}
*values[j] = &element->value;
}

/* check if any of the required keys are missing */
for (i = 0; i < num_values && keys[i].is_required; ++i) {
if (*values[i] == NULL) {
h2o_configurator_errprintf(cmd, node, "cannot find mandatory attribute: %.*s", (int)keys[i].key.len, keys[i].key.base);
return -1;
}
}

return 0;
}

char *h2o_configurator_get_cmd_path(const char *cmd)
{
char *root, *cmd_fullpath;
Expand Down
55 changes: 19 additions & 36 deletions lib/handler/configurator/access_log.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,55 +33,38 @@ struct st_h2o_access_log_configurator_t {
static int on_config(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
{
struct st_h2o_access_log_configurator_t *self = (void *)cmd->configurator;
const char *path, *fmt = NULL;
yoml_t **path, **format = NULL, **escape_node = NULL;
int escape = H2O_LOGCONF_ESCAPE_APACHE;
h2o_access_log_filehandle_t *fh;

switch (node->type) {
case YOML_TYPE_SCALAR:
path = node->data.scalar;
path = &node;
break;
case YOML_TYPE_MAPPING: {
yoml_t *t;
/* get path */
if ((t = yoml_get(node, "path")) == NULL) {
h2o_configurator_errprintf(cmd, node, "could not find mandatory key `path`");
case YOML_TYPE_MAPPING:
if (h2o_configurator_parse_mapping(cmd, node, "path:s", "format:s,escape:*", &path, &format, &escape_node) != 0)
return -1;
}
if (t->type != YOML_TYPE_SCALAR) {
h2o_configurator_errprintf(cmd, t, "`path` must be scalar");
return -1;
}
path = t->data.scalar;
/* get format */
if ((t = yoml_get(node, "format")) != NULL) {
if (t->type != YOML_TYPE_SCALAR) {
h2o_configurator_errprintf(cmd, t, "`format` must be a scalar");
return -1;
}
fmt = t->data.scalar;
}
/* get escape */
if ((t = yoml_get(node, "escape")) != NULL) {
switch (h2o_configurator_get_one_of(cmd, t, "apache,json")) {
case 0:
escape = H2O_LOGCONF_ESCAPE_APACHE;
break;
case 1:
escape = H2O_LOGCONF_ESCAPE_JSON;
break;
default:
return -1;
}
}
} break;
break;
default:
h2o_configurator_errprintf(cmd, node, "node must be a scalar or a mapping");
return -1;
}

if (escape_node != NULL) {
switch (h2o_configurator_get_one_of(cmd, *escape_node, "apache,json")) {
case 0:
escape = H2O_LOGCONF_ESCAPE_APACHE;
break;
case 1:
escape = H2O_LOGCONF_ESCAPE_JSON;
break;
default:
return -1;
}
}

if (!ctx->dry_run) {
if ((fh = h2o_access_log_open_handle(path, fmt, escape)) == NULL)
if ((fh = h2o_access_log_open_handle((*path)->data.scalar, format != NULL ? (*format)->data.scalar : NULL, escape)) == NULL)
return -1;
h2o_vector_reserve(NULL, self->handles, self->handles->size + 1);
self->handles->entries[self->handles->size++] = fh;
Expand Down
35 changes: 14 additions & 21 deletions lib/handler/configurator/compress.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,29 +103,22 @@ static int on_config_compress(h2o_configurator_command_t *cmd, h2o_configurator_
}
}
break;
case YOML_TYPE_MAPPING:
case YOML_TYPE_MAPPING: {
yoml_t **gzip_node, **br_node;
*self->vars = all_off;
for (i = 0; i != node->data.mapping.size; ++i) {
yoml_t *key = node->data.mapping.elements[i].key;
yoml_t *value = node->data.mapping.elements[i].value;
if (key->type == YOML_TYPE_SCALAR && strcasecmp(key->data.scalar, "gzip") == 0) {
if (obtain_quality(value, 1, 9, DEFAULT_GZIP_QUALITY, &self->vars->gzip.quality) != 0) {
h2o_configurator_errprintf(
cmd, value, "value of gzip attribute must be either of `OFF`, `ON` or an integer value between 1 and 9");
return -1;
}
} else if (key->type == YOML_TYPE_SCALAR && strcasecmp(key->data.scalar, "br") == 0) {
if (obtain_quality(value, 0, 11, DEFAULT_BROTLI_QUALITY, &self->vars->brotli.quality) != 0) {
h2o_configurator_errprintf(
cmd, value, "value of br attribute must be either of `OFF`, `ON` or an integer between 0 and 11");
return -1;
}
} else {
h2o_configurator_errprintf(cmd, key, "key must be either of: `gzip`, `br`");
return -1;
}
if (h2o_configurator_parse_mapping(cmd, node, NULL, "gzip:*,br:*", &gzip_node, &br_node) != 0)
return -1;
if (gzip_node != NULL && obtain_quality(*gzip_node, 1, 9, DEFAULT_GZIP_QUALITY, &self->vars->gzip.quality) != 0) {
h2o_configurator_errprintf(cmd, *gzip_node,
"value of gzip attribute must be either of `OFF`, `ON` or an integer value between 1 and 9");
return -1;
}
break;
if (br_node != NULL && obtain_quality(*br_node, 0, 11, DEFAULT_BROTLI_QUALITY, &self->vars->brotli.quality) != 0) {
h2o_configurator_errprintf(cmd, *br_node,
"value of br attribute must be either of `OFF`, `ON` or an integer between 0 and 11");
return -1;
}
} break;
default:
h2o_fatal("unexpected node type");
break;
Expand Down
Loading

0 comments on commit 74a13a2

Please sign in to comment.