diff --git a/src/base.h b/src/base.h index af95dc30..d4954197 100644 --- a/src/base.h +++ b/src/base.h @@ -13,7 +13,6 @@ #include "chunk.h" #include "http_kv.h" #include "sock_addr.h" -#include "etag.h" struct fdevents; /* declaration */ struct stat_cache; /* declaration */ @@ -76,57 +75,52 @@ typedef struct { } physical; typedef struct { - array *mimetypes; + const array *mimetypes; /* virtual-servers */ - buffer *document_root; - buffer *server_name; - buffer *error_handler; - buffer *error_handler_404; - buffer *server_tag; - buffer *errorfile_prefix; - - unsigned short high_precision_timestamps; + const buffer *document_root; + const buffer *server_name; + const buffer *server_tag; + const buffer *error_handler; + const buffer *error_handler_404; + const buffer *errorfile_prefix; + unsigned short max_keep_alive_requests; unsigned short max_keep_alive_idle; unsigned short max_read_idle; unsigned short max_write_idle; - unsigned short use_xattr; - unsigned short follow_symlink; - unsigned short range_requests; unsigned short stream_request_body; unsigned short stream_response_body; - unsigned short error_intercept; + unsigned char high_precision_timestamps; + unsigned char allow_http11; + unsigned char follow_symlink; + unsigned char etag_flags; + unsigned char force_lowercase_filenames; /* if the FS is case-insensitive, force all files to lower-case */ + unsigned char use_xattr; + unsigned char range_requests; + unsigned char error_intercept; /* debug */ - unsigned short log_file_not_found; - unsigned short log_request_header; - unsigned short log_request_handling; - unsigned short log_response_header; - unsigned short log_condition_handling; - unsigned short log_timeouts; - - unsigned short allow_http11; - unsigned short etag_use_inode; - unsigned short etag_use_mtime; - unsigned short etag_use_size; - unsigned short force_lowercase_filenames; /* if the FS is case-insensitive, force all files to lower-case */ + unsigned char log_file_not_found; + unsigned char log_request_header; + unsigned char log_request_handling; + unsigned char log_response_header; + unsigned char log_condition_handling; + unsigned char log_timeouts; + unsigned int http_parseopts; unsigned int max_request_size; - unsigned short kbytes_per_second; /* connection kb/s limit */ - - /* configside */ - unsigned short global_kbytes_per_second; /* */ + unsigned int bytes_per_second; /* connection bytes/sec limit */ + unsigned int global_bytes_per_second;/*total bytes/sec limit for scope*/ - off_t global_bytes_per_second_cnt; /* server-wide traffic-shaper * * each context has the counter which is inited once - * a second by the global_kbytes_per_second config-var + * a second by the global_bytes_per_second config-var * - * as soon as global_kbytes_per_second gets below 0 + * as soon as global_bytes_per_second gets below 0 * the connected conns are "offline" a little bit * * the problem: @@ -135,25 +129,6 @@ typedef struct { * */ off_t *global_bytes_per_second_cnt_ptr; /* */ - - /* - * global_bytes_per_second_cnt_ptr must be the final member above this point - * members above this point are patched per connection - */ - - /* global or per-socket config; not patched per connection */ - - unsigned short use_ipv6, set_v6only; /* set_v6only is only a temporary option */ - unsigned short defer_accept; - unsigned short ssl_enabled; /* only interesting for setting up listening sockets. don't use at runtime */ - int listen_backlog; - buffer *socket_perms; - -#if defined(__FreeBSD__) || defined(__NetBSD__) \ - || defined(__OpenBSD__) || defined(__DragonFly__) - buffer *bsd_accept_filter; -#endif - } specific_config; /* the order of the items should be the same as they are processed @@ -193,9 +168,9 @@ typedef struct cond_cache_t { cond_result_t result; /* result without preconditions (must never be "skip") */ cond_result_t local_result; + const buffer *comp_value; /* just a pointer */ int patterncount; int matches[3 * 10]; - const buffer *comp_value; /* just a pointer */ } cond_cache_t; struct connection { @@ -280,9 +255,6 @@ struct connection { struct server_socket *srv_socket; /* reference to the server-socket */ int (* network_write)(struct server *srv, struct connection *con, chunkqueue *cq, off_t max_bytes); int (* network_read)(struct server *srv, struct connection *con, chunkqueue *cq, off_t max_bytes); - - /* etag handling */ - etag_flags_t etag_flags; }; typedef struct { @@ -297,56 +269,52 @@ typedef struct { } mtime_cache_type; typedef struct { - void *ptr; + void *ptr; uint32_t used; uint32_t size; } buffer_plugin; typedef struct { unsigned int max_request_field_size; + int stat_cache_engine; + const char *xattr_name; + unsigned int log_state_handling; + unsigned char log_request_header_on_error; + + /*(used sparsely, if at all, after config at startup)*/ - unsigned short http_header_strict; - unsigned short http_host_strict; - unsigned short http_host_normalize; + unsigned char http_header_strict; + unsigned char http_host_strict; + unsigned char http_host_normalize; + unsigned char http_method_get_body; + unsigned char high_precision_timestamps; unsigned short http_url_normalize; - unsigned short http_method_get_body; - unsigned short high_precision_timestamps; unsigned short max_worker; unsigned short max_fds; unsigned short max_conns; - - unsigned short log_state_handling; - unsigned short log_request_header_on_error; unsigned short port; - time_t loadts; - double loadavg[3]; - - int stat_cache_engine; - unsigned int upload_temp_file_size; array *upload_tempdirs; - buffer *xattr_name; - - unsigned short dont_daemonize; - unsigned short preflight_check; - unsigned short enable_cores; - unsigned short reject_expect_100_with_417; /*(ignored)*/ - unsigned short compat_module_load; - unsigned short systemd_socket_activation; - unsigned short errorlog_use_syslog; - buffer *errorlog_file; - buffer *breakagelog_file; - buffer *syslog_facility; - buffer *bindhost; - buffer *changeroot; - buffer *username; - buffer *groupname; + + unsigned char dont_daemonize; + unsigned char preflight_check; + unsigned char enable_cores; + unsigned char compat_module_load; + unsigned char systemd_socket_activation; + unsigned char errorlog_use_syslog; + const buffer *errorlog_file; + const buffer *breakagelog_file; + const buffer *syslog_facility; + const buffer *bindhost; + const buffer *changeroot; + const buffer *username; + const buffer *groupname; + const buffer *network_backend; + const char *event_handler; buffer *pid_file; - buffer *event_handler; buffer *modules_dir; - buffer *network_backend; array *modules; } server_config; @@ -406,10 +374,6 @@ struct server { buffer *ts_date_str; log_error_st *errh; - /* config-file */ - array *config_context; - specific_config **config_storage; - /** * The status array can carry all the status information you want * the key to the array is . @@ -430,14 +394,22 @@ struct server { /* caches */ mtime_cache_type mtime_cache[FILE_CACHE_MAX]; + time_t loadts; + double loadavg[3]; + + /* config-file */ + void *config_data_base; + array *config_context; + array empty_array; + /* members used at start-up or rarely used */ server_socket_array srv_sockets; server_socket_array srv_sockets_inherited; buffer_plugin plugins; array *config_touched; - short int config_deprecated; - short int config_unsupported; + unsigned char config_deprecated; + unsigned char config_unsupported; int event_handler; time_t startup_ts; diff --git a/src/chunk.c b/src/chunk.c index 1d67e7e4..d1095c0f 100644 --- a/src/chunk.c +++ b/src/chunk.c @@ -28,7 +28,7 @@ static size_t chunk_buf_sz = 4096; static chunk *chunks, *chunks_oversized; static chunk *chunk_buffers; -static array *chunkqueue_default_tempdirs = NULL; +static const array *chunkqueue_default_tempdirs = NULL; static off_t chunkqueue_default_tempfile_size = DEFAULT_TEMPFILE_SIZE; void chunkqueue_set_chunk_size (size_t sz) @@ -438,7 +438,7 @@ void chunkqueue_use_memory(chunkqueue *cq, size_t len) { } } -void chunkqueue_set_tempdirs_default (array *tempdirs, off_t upload_temp_file_size) { +void chunkqueue_set_tempdirs_default (const array *tempdirs, off_t upload_temp_file_size) { chunkqueue_default_tempdirs = tempdirs; chunkqueue_default_tempfile_size = (0 == upload_temp_file_size) ? DEFAULT_TEMPFILE_SIZE @@ -446,7 +446,7 @@ void chunkqueue_set_tempdirs_default (array *tempdirs, off_t upload_temp_file_si : upload_temp_file_size; } -void chunkqueue_set_tempdirs(chunkqueue *cq, array *tempdirs, off_t upload_temp_file_size) { +void chunkqueue_set_tempdirs(chunkqueue *cq, const array *tempdirs, off_t upload_temp_file_size) { force_assert(NULL != cq); cq->tempdirs = tempdirs; cq->upload_temp_file_size diff --git a/src/chunk.h b/src/chunk.h index 500dc960..bf0a39fe 100644 --- a/src/chunk.h +++ b/src/chunk.h @@ -42,7 +42,7 @@ typedef struct { off_t bytes_in, bytes_out; - array *tempdirs; + const array *tempdirs; off_t upload_temp_file_size; unsigned int tempdir_idx; } chunkqueue; @@ -60,8 +60,8 @@ chunkqueue *chunkqueue_init(void); void chunkqueue_set_chunk_size (size_t sz); void chunkqueue_set_tempdirs_default_reset (void); -void chunkqueue_set_tempdirs_default (array *tempdirs, off_t upload_temp_file_size); -void chunkqueue_set_tempdirs(chunkqueue *cq, array *tempdirs, off_t upload_temp_file_size); +void chunkqueue_set_tempdirs_default (const array *tempdirs, off_t upload_temp_file_size); +void chunkqueue_set_tempdirs(chunkqueue *cq, const array *tempdirs, off_t upload_temp_file_size); void chunkqueue_append_file(chunkqueue *cq, buffer *fn, off_t offset, off_t len); /* copies "fn" */ void chunkqueue_append_file_fd(chunkqueue *cq, buffer *fn, int fd, off_t offset, off_t len); /* copies "fn" */ void chunkqueue_append_mem(chunkqueue *cq, const char *mem, size_t len); /* copies memory */ diff --git a/src/configfile.c b/src/configfile.c index f9a0af40..e6753648 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -9,6 +9,7 @@ #include "configparser.h" #include "configfile.h" +#include "plugin.h" #include "stat_cache.h" #include "sys-crypto.h" @@ -28,6 +29,206 @@ #define PATH_MAX 4096 #endif +typedef struct { + PLUGIN_DATA; + specific_config defaults; +} config_data_base; + +void config_free_config(void * const p_d) { + plugin_data_base * const p = p_d; + if (NULL == p) return; + if (NULL == p->cvlist) { free(p); return; } + /* (init i to 0 if global context; to 1 to skip empty global context) */ + for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { + config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; + for (; -1 != cpv->k_id; ++cpv) { + switch (cpv->k_id) { + case 18:/* server.kbytes-per-second */ + if (cpv->vtype == T_CONFIG_LOCAL) free(cpv->v.v); + break; + default: + break; + } + } + } + free(p->cvlist); + free(p); +} + +void config_reset_config_bytes_sec(void * const p_d) { + plugin_data_base * const p = p_d; + if (NULL == p->cvlist) return; + /* (init i to 0 if global context; to 1 to skip empty global context) */ + for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { + config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; + for (; -1 != cpv->k_id; ++cpv) { + switch (cpv->k_id) { + case 18:/* server.kbytes-per-second */ + if (cpv->vtype == T_CONFIG_LOCAL) ((off_t *)cpv->v.v)[0] = 0; + break; + default: + break; + } + } + } +} + +static void config_merge_config_cpv(specific_config * const pconf, const config_plugin_value_t * const cpv) { + switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ + case 0: /* server.document-root */ + pconf->document_root = cpv->v.b; + break; + case 1: /* server.name */ + pconf->server_name = cpv->v.b; + break; + case 2: /* server.tag */ + pconf->server_tag = cpv->v.b; + break; + case 3: /* server.max-request-size */ + pconf->max_request_size = cpv->v.u; + break; + case 4: /* server.max-keep-alive-requests */ + pconf->max_keep_alive_requests = cpv->v.shrt; + break; + case 5: /* server.max-keep-alive-idle */ + pconf->max_keep_alive_idle = cpv->v.shrt; + break; + case 6: /* server.max-read-idle */ + pconf->max_read_idle = cpv->v.shrt; + break; + case 7: /* server.max-write-idle */ + pconf->max_write_idle = cpv->v.shrt; + break; + case 8: /* server.errorfile-prefix */ + pconf->errorfile_prefix = cpv->v.b; + break; + case 9: /* server.error-handler */ + pconf->error_handler = cpv->v.b; + break; + case 10:/* server.error-handler-404 */ + pconf->error_handler_404 = cpv->v.b; + break; + case 11:/* server.error-intercept */ + pconf->error_intercept = (0 != cpv->v.u); + break; + case 12:/* server.force-lowercase-filenames */ + pconf->force_lowercase_filenames = (0 != cpv->v.u); + break; + case 13:/* server.follow-symlink */ + pconf->follow_symlink = (0 != cpv->v.u); + break; + case 14:/* server.protocol-http11 */ + pconf->allow_http11 = (0 != cpv->v.u); + break; + case 15:/* server.range-requests */ + pconf->range_requests = (0 != cpv->v.u); + break; + case 16:/* server.stream-request-body */ + pconf->stream_request_body = cpv->v.shrt; + break; + case 17:/* server.stream-response-body */ + pconf->stream_response_body = cpv->v.shrt; + break; + case 18:/* server.kbytes-per-second */ + pconf->global_bytes_per_second = (unsigned int)((off_t *)cpv->v.v)[1]; + pconf->global_bytes_per_second_cnt_ptr = cpv->v.v; + break; + case 19:/* connection.kbytes-per-second */ + pconf->bytes_per_second = (unsigned int)cpv->v.shrt << 10;/* (*=1024) */ + break; + case 20:/* mimetype.assign */ + pconf->mimetypes = cpv->v.a; + break; + case 21:/* mimetype.use-xattr */ + pconf->use_xattr = (0 != cpv->v.u); + break; + case 22:/* etag.use-inode */ + cpv->v.u + ? (pconf->etag_flags |= ETAG_USE_INODE) + : (pconf->etag_flags &= ~ETAG_USE_INODE); + break; + case 23:/* etag.use-mtime */ + cpv->v.u + ? (pconf->etag_flags |= ETAG_USE_MTIME) + : (pconf->etag_flags &= ~ETAG_USE_MTIME); + break; + case 24:/* etag.use-size */ + cpv->v.u + ? (pconf->etag_flags |= ETAG_USE_SIZE) + : (pconf->etag_flags &= ~ETAG_USE_SIZE); + break; + case 25:/* debug.log-condition-handling */ + pconf->log_condition_handling = (0 != cpv->v.u); + break; + case 26:/* debug.log-file-not-found */ + pconf->log_file_not_found = (0 != cpv->v.u); + break; + case 27:/* debug.log-request-handling */ + pconf->log_request_handling = (0 != cpv->v.u); + break; + case 28:/* debug.log-request-header */ + pconf->log_request_header = (0 != cpv->v.u); + break; + case 29:/* debug.log-response-header */ + pconf->log_response_header = (0 != cpv->v.u); + break; + case 30:/* debug.log-timeouts */ + pconf->log_timeouts = (0 != cpv->v.u); + break; + default:/* should not happen */ + return; + } +} + +static void config_merge_config(specific_config * const pconf, const config_plugin_value_t *cpv) { + do { + config_merge_config_cpv(pconf, cpv); + } while ((++cpv)->k_id != -1); +} + +void config_patch_config(server * const srv, connection * const con) { + config_data_base * const p = srv->config_data_base; + + /* performed by config_reset_config() */ + /*memcpy(&con->conf, &p->defaults, sizeof(specific_config));*/ + + for (int i = 1, used = p->nconfig; i < used; ++i) { + if (config_check_cond(con, (uint32_t)p->cvlist[i].k_id)) + config_merge_config(&con->conf, p->cvlist + p->cvlist[i].v.u2[0]); + } +} + +void config_reset_config(server * const srv, connection * const con) { + /* initialize specific_config (con->conf) from top-level specific_config */ + config_data_base * const p = srv->config_data_base; + con->server_name = p->defaults.server_name; + memcpy(&con->conf, &p->defaults, sizeof(specific_config)); +} + +static int config_burl_normalize_cond (server *srv) { + for (uint32_t i = 0; i < srv->config_context->used; ++i) { + data_config * const config =(data_config *)srv->config_context->data[i]; + if (COMP_HTTP_QUERY_STRING != config->comp) continue; + switch(config->cond) { + case CONFIG_COND_NE: + case CONFIG_COND_EQ: + /* (can use this routine as long as it does not perform + * any regex-specific normalization of first arg) */ + pcre_keyvalue_burl_normalize_key(&config->string, srv->tmp_buf); + break; + case CONFIG_COND_NOMATCH: + case CONFIG_COND_MATCH: + pcre_keyvalue_burl_normalize_key(&config->string, srv->tmp_buf); + if (!data_config_pcre_compile(config)) return 0; + break; + default: + break; + } + } + + return 1; +} + #if defined(HAVE_MYSQL) || (defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER)) static void config_warn_authn_module (server *srv, const char *module, size_t len) { for (size_t i = 0; i < srv->config_context->used; ++i) { @@ -64,71 +265,165 @@ static void config_warn_openssl_module (server *srv) { } #endif -static int config_http_parseopts (server *srv, array *a) { +static void config_compat_module_load (server *srv) { + int prepend_mod_indexfile = 1; + int append_mod_dirlisting = 1; + int append_mod_staticfile = 1; + int append_mod_authn_file = 1; + int append_mod_authn_ldap = 1; + int append_mod_authn_mysql = 1; + int append_mod_openssl = 1; + int contains_mod_auth = 0; + + for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) { + buffer *m = &((data_string *)srv->srvconf.modules->data[i])->value; + + if (buffer_eq_slen(m, CONST_STR_LEN("mod_indexfile"))) + prepend_mod_indexfile = 0; + else if (buffer_eq_slen(m, CONST_STR_LEN("mod_staticfile"))) + append_mod_staticfile = 0; + else if (buffer_eq_slen(m, CONST_STR_LEN("mod_dirlisting"))) + append_mod_dirlisting = 0; + else if (buffer_eq_slen(m, CONST_STR_LEN("mod_openssl"))) + append_mod_openssl = 0; + else if (buffer_eq_slen(m, CONST_STR_LEN("mod_authn_file"))) + append_mod_authn_file = 0; + else if (buffer_eq_slen(m, CONST_STR_LEN("mod_authn_ldap"))) + append_mod_authn_ldap = 0; + else if (buffer_eq_slen(m, CONST_STR_LEN("mod_authn_mysql"))) + append_mod_authn_mysql = 0; + else if (buffer_eq_slen(m, CONST_STR_LEN("mod_auth"))) + contains_mod_auth = 1; + + if (0 == prepend_mod_indexfile && + 0 == append_mod_dirlisting && + 0 == append_mod_staticfile && + 0 == append_mod_openssl && + 0 == append_mod_authn_file && + 0 == append_mod_authn_ldap && + 0 == append_mod_authn_mysql && + 1 == contains_mod_auth) { + break; + } + } + + /* prepend default modules */ + + if (prepend_mod_indexfile) { + /* mod_indexfile has to be loaded before mod_fastcgi and friends */ + array *modules = array_init(); + array_insert_value(modules, CONST_STR_LEN("mod_indexfile")); + + for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) { + data_string *ds = (data_string *)srv->srvconf.modules->data[i]; + array_insert_value(modules, CONST_BUF_LEN(&ds->value)); + } + + array_free(srv->srvconf.modules); + srv->srvconf.modules = modules; + } + + /* append default modules */ + + if (append_mod_dirlisting) { + array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_dirlisting")); + } + + if (append_mod_staticfile) { + array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_staticfile")); + } + + if (append_mod_openssl) { + #ifdef USE_OPENSSL_CRYPTO + config_warn_openssl_module(srv); + #endif + } + + /* mod_auth.c,http_auth.c auth backends were split into separate modules + * Automatically load auth backend modules for compatibility with + * existing lighttpd 1.4.x configs */ + if (contains_mod_auth) { + if (append_mod_authn_file) { + array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_authn_file")); + } + if (append_mod_authn_ldap) { + #if defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER) + config_warn_authn_module(srv, CONST_STR_LEN("ldap")); + #endif + } + if (append_mod_authn_mysql) { + #if defined(HAVE_MYSQL) + config_warn_authn_module(srv, CONST_STR_LEN("mysql")); + #endif + } + } +} + +static int config_http_parseopts (server *srv, const array *a) { unsigned short int opts = srv->srvconf.http_url_normalize; unsigned short int decode_2f = 1; int rc = 1; if (!array_is_kvstring(a)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "unexpected value for server.http-parseopts; " - "expected list of \"key\" => \"[enable|disable]\""); + log_error(srv->errh, __FILE__, __LINE__, + "unexpected value for server.http-parseopts; " + "expected list of \"key\" => \"[enable|disable]\""); return 0; } for (size_t i = 0; i < a->used; ++i) { - const data_string * const ds = (data_string *)a->data[i]; + const data_string * const ds = (const data_string *)a->data[i]; + const buffer *k = &ds->key; + const buffer *v = &ds->value; unsigned short int opt; int val = 0; - if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("enable"))) + if (buffer_eq_slen(v, CONST_STR_LEN("enable"))) val = 1; - else if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("disable"))) + else if (buffer_eq_slen(v, CONST_STR_LEN("disable"))) val = 0; else { - log_error_write(srv, __FILE__, __LINE__, "sbsbs", - "unrecognized value for server.http-parseopts:", - &ds->key, "=>", &ds->value, - "(expect \"[enable|disable]\")"); + log_error(srv->errh, __FILE__, __LINE__, + "unrecognized value for server.http-parseopts: " + "%s => %s (expect \"[enable|disable]\")", k->ptr, v->ptr); rc = 0; } - if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("url-normalize"))) + if (buffer_eq_slen(k, CONST_STR_LEN("url-normalize"))) opt = HTTP_PARSEOPT_URL_NORMALIZE; - else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("url-normalize-unreserved"))) + else if (buffer_eq_slen(k, CONST_STR_LEN("url-normalize-unreserved"))) opt = HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED; - else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("url-normalize-required"))) + else if (buffer_eq_slen(k, CONST_STR_LEN("url-normalize-required"))) opt = HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED; - else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("url-ctrls-reject"))) + else if (buffer_eq_slen(k, CONST_STR_LEN("url-ctrls-reject"))) opt = HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT; - else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("url-path-backslash-trans"))) + else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-backslash-trans"))) opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_BACKSLASH_TRANS; - else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("url-path-2f-decode"))) + else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-2f-decode"))) opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE; - else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("url-path-2f-reject"))) + else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-2f-reject"))) opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT; - else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("url-path-dotseg-remove"))) + else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-dotseg-remove"))) opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE; - else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("url-path-dotseg-reject"))) + else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-dotseg-reject"))) opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT; - else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("url-query-20-plus"))) + else if (buffer_eq_slen(k, CONST_STR_LEN("url-query-20-plus"))) opt = HTTP_PARSEOPT_URL_NORMALIZE_QUERY_20_PLUS; - else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("header-strict"))) { + else if (buffer_eq_slen(k, CONST_STR_LEN("header-strict"))) { srv->srvconf.http_header_strict = val; continue; } - else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("host-strict"))) { + else if (buffer_eq_slen(k, CONST_STR_LEN("host-strict"))) { srv->srvconf.http_host_strict = val; continue; } - else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("host-normalize"))) { + else if (buffer_eq_slen(k, CONST_STR_LEN("host-normalize"))) { srv->srvconf.http_host_normalize = val; continue; } - else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("method-get-body"))) { + else if (buffer_eq_slen(k, CONST_STR_LEN("method-get-body"))) { srv->srvconf.http_method_get_body = val; continue; } else { - log_error_write(srv, __FILE__, __LINE__, "sb", - "unrecognized key for server.http-parseopts:", - &ds->key); + log_error(srv->errh, __FILE__, __LINE__, + "unrecognized key for server.http-parseopts: %s", k->ptr); rc = 0; continue; } @@ -151,18 +446,18 @@ static int config_http_parseopts (server *srv, array *a) { |HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT)) == (HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE |HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "conflicting options in server.http-parseopts:" - "url-path-2f-decode, url-path-2f-reject"); + log_error(srv->errh, __FILE__, __LINE__, + "conflicting options in server.http-parseopts:" + "url-path-2f-decode, url-path-2f-reject"); rc = 0; } if ((opts & (HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE |HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT)) == (HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE |HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "conflicting options in server.http-parseopts:" - "url-path-dotseg-remove, url-path-dotseg-reject"); + log_error(srv->errh, __FILE__, __LINE__, + "conflicting options in server.http-parseopts:" + "url-path-dotseg-remove, url-path-dotseg-reject"); rc = 0; } if (!(opts & (HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED @@ -177,592 +472,512 @@ static int config_http_parseopts (server *srv, array *a) { return rc; } -static int config_insert(server *srv) { - size_t i; - int ret = 0; - buffer *stat_cache_string; - array *http_parseopts; - unsigned int chunk_sz = 0; - - config_values_t cv[] = { - { "server.bind", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 0 */ - { "server.errorlog", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 1 */ - { "server.errorfile-prefix", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ - { "server.chroot", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 3 */ - { "server.username", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 4 */ - { "server.groupname", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 5 */ - { "server.port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 6 */ - { "server.tag", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ - { "server.use-ipv6", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ - { "server.modules", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_SERVER }, /* 9 */ - - { "server.event-handler", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 10 */ - { "server.pid-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 11 */ - { "server.max-request-size", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ - { "server.max-worker", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 13 */ - { "server.document-root", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 14 */ - { "server.force-lowercase-filenames", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */ - { "debug.log-condition-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 16 */ - { "server.max-keep-alive-requests", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 17 */ - { "server.name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 18 */ - { "server.max-keep-alive-idle", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 19 */ - - { "server.max-read-idle", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 20 */ - { "server.max-write-idle", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 21 */ - { "server.error-handler", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 22 */ - { "server.max-fds", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 23 */ -#ifdef HAVE_LSTAT - { "server.follow-symlink", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 24 */ -#else - { "server.follow-symlink", - "Your system lacks lstat(). We can not differ symlinks from files." - "Please remove server.follow-symlinks from your config.", - T_CONFIG_UNSUPPORTED, T_CONFIG_SCOPE_UNSET }, -#endif - { "server.kbytes-per-second", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 25 */ - { "connection.kbytes-per-second", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 26 */ - { "mimetype.use-xattr", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 27 */ - { "mimetype.assign", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 28 */ - { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 29 */ - - { "ssl.engine", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 30 */ - { "debug.log-file-not-found", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 31 */ - { "debug.log-request-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 32 */ - { "debug.log-response-header", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 33 */ - { "debug.log-request-header", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 34 */ - { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 35 */ - { "server.protocol-http11", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 36 */ - { "debug.log-request-header-on-error", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 37 */ - { "debug.log-state-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 38 */ - { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 39 */ - - { "server.errorlog-use-syslog", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 40 */ - { "server.range-requests", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 41 */ - { "server.stat-cache-engine", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 42 */ - { "server.max-connections", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 43 */ - { "server.network-backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 44 */ - { "server.upload-dirs", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_SERVER }, /* 45 */ - { "server.core-files", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 46 */ - { "server.compat-module-load", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 47 */ - { "server.chunkqueue-chunk-sz", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_SERVER }, /* 48 */ - { "etag.use-inode", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 49 */ - - { "etag.use-mtime", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 50 */ - { "etag.use-size", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 51 */ - { "server.reject-expect-100-with-417", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 52 */ - { "debug.log-timeouts", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 53 */ - { "server.defer-accept", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 54 */ - { "server.breakagelog", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 55 */ - { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 56 */ - { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 57 */ - { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 58 */ - { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 59 */ - - { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 60 */ - { "server.set-v6only", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 61 */ - { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 62 */ - { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 63 */ - { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 64 */ - { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 65 */ - { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 66 */ - { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 67 */ - { "server.upload-temp-file-size", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_SERVER }, /* 68 */ - { "mimetype.xattr-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 69 */ - { "server.listen-backlog", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 70 */ - { "server.error-handler-404", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 71 */ - { "server.http-parseopt-header-strict",NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 72 */ - { "server.http-parseopt-host-strict", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 73 */ - { "server.http-parseopt-host-normalize",NULL,T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 74 */ - { "server.bsd-accept-filter", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 75 */ - { "server.stream-request-body", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 76 */ - { "server.stream-response-body", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 77 */ - { "server.max-request-field-size", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_SERVER }, /* 78 */ - { "server.error-intercept", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 79 */ - { "server.syslog-facility", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 80 */ - { "server.socket-perms", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 81 */ - { "server.http-parseopts", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_SERVER }, /* 82 */ - { "server.systemd-socket-activation", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 83 */ - - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - /* all T_CONFIG_SCOPE_SERVER options */ - cv[0].destination = srv->srvconf.bindhost; - cv[1].destination = srv->srvconf.errorlog_file; - cv[3].destination = srv->srvconf.changeroot; - cv[4].destination = srv->srvconf.username; - cv[5].destination = srv->srvconf.groupname; - cv[6].destination = &(srv->srvconf.port); - cv[9].destination = srv->srvconf.modules; - - cv[10].destination = srv->srvconf.event_handler; - cv[11].destination = srv->srvconf.pid_file; - cv[13].destination = &(srv->srvconf.max_worker); - - cv[23].destination = &(srv->srvconf.max_fds); - - cv[37].destination = &(srv->srvconf.log_request_header_on_error); - cv[38].destination = &(srv->srvconf.log_state_handling); - - cv[40].destination = &(srv->srvconf.errorlog_use_syslog); - stat_cache_string = buffer_init(); - cv[42].destination = stat_cache_string; - cv[43].destination = &(srv->srvconf.max_conns); - cv[44].destination = srv->srvconf.network_backend; - cv[45].destination = srv->srvconf.upload_tempdirs; - cv[46].destination = &(srv->srvconf.enable_cores); - cv[47].destination = &(srv->srvconf.compat_module_load); - cv[48].destination = &chunk_sz; - - cv[52].destination = &(srv->srvconf.reject_expect_100_with_417); - cv[55].destination = srv->srvconf.breakagelog_file; - - cv[68].destination = &(srv->srvconf.upload_temp_file_size); - cv[69].destination = srv->srvconf.xattr_name; - cv[72].destination = &(srv->srvconf.http_header_strict); - cv[73].destination = &(srv->srvconf.http_host_strict); - cv[74].destination = &(srv->srvconf.http_host_normalize); - cv[78].destination = &(srv->srvconf.max_request_field_size); - cv[80].destination = srv->srvconf.syslog_facility; - http_parseopts = array_init(); - cv[82].destination = http_parseopts; - cv[83].destination = &(srv->srvconf.systemd_socket_activation); - - srv->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - force_assert(srv->config_storage); - force_assert(srv->config_context->used); /* static analysis hint for ccc --analyzer */ - - for (i = 0; i < srv->config_context->used; i++) { - data_config * const config = (data_config *)srv->config_context->data[i]; - specific_config *s; - - s = calloc(1, sizeof(specific_config)); - force_assert(s); - s->document_root = buffer_init(); - s->mimetypes = array_init(); - s->server_name = buffer_init(); - s->error_handler = buffer_init(); - s->error_handler_404 = buffer_init(); - s->server_tag = buffer_init(); - s->errorfile_prefix = buffer_init(); - #if defined(__FreeBSD__) || defined(__NetBSD__) \ - || defined(__OpenBSD__) || defined(__DragonFly__) - s->bsd_accept_filter = (i == 0) - ? buffer_init() - : buffer_init_buffer(srv->config_storage[0]->bsd_accept_filter); - #endif - s->socket_perms = (i == 0 || buffer_string_is_empty(srv->config_storage[0]->socket_perms)) - ? buffer_init() - : buffer_init_buffer(srv->config_storage[0]->socket_perms); - s->max_keep_alive_requests = 100; - s->max_keep_alive_idle = 5; - s->max_read_idle = 60; - s->max_write_idle = 360; - s->max_request_size = 0; - s->use_xattr = 0; - s->ssl_enabled = 0; - s->use_ipv6 = (i == 0) ? 0 : srv->config_storage[0]->use_ipv6; - s->set_v6only = (i == 0) ? 1 : srv->config_storage[0]->set_v6only; - s->defer_accept = (i == 0) ? 0 : srv->config_storage[0]->defer_accept; - s->follow_symlink = 1; - s->kbytes_per_second = 0; - s->allow_http11 = 1; - s->etag_use_inode = 1; - s->etag_use_mtime = 1; - s->etag_use_size = 1; - s->range_requests = 1; - s->force_lowercase_filenames = (i == 0) ? 2 : 0; /* we wan't to detect later if user changed this for global section */ - s->global_kbytes_per_second = 0; - s->global_bytes_per_second_cnt = 0; - s->global_bytes_per_second_cnt_ptr = &s->global_bytes_per_second_cnt; - s->listen_backlog = (0 == i ? 1024 : srv->config_storage[0]->listen_backlog); - s->stream_request_body = 0; - s->stream_response_body = 0; - s->error_intercept = 0; - - /* all T_CONFIG_SCOPE_CONNECTION options */ - cv[2].destination = s->errorfile_prefix; - cv[7].destination = s->server_tag; - cv[8].destination = &(s->use_ipv6); - - cv[12].destination = &(s->max_request_size); - cv[14].destination = s->document_root; - cv[15].destination = &(s->force_lowercase_filenames); - cv[16].destination = &(s->log_condition_handling); - cv[17].destination = &(s->max_keep_alive_requests); - cv[18].destination = s->server_name; - cv[19].destination = &(s->max_keep_alive_idle); - - cv[20].destination = &(s->max_read_idle); - cv[21].destination = &(s->max_write_idle); - cv[22].destination = s->error_handler; - cv[24].destination = &(s->follow_symlink); - cv[25].destination = &(s->global_kbytes_per_second); - cv[26].destination = &(s->kbytes_per_second); - cv[27].destination = &(s->use_xattr); - cv[28].destination = s->mimetypes; - /*cv[29].destination = s->unused;*/ - - cv[30].destination = &(s->ssl_enabled); - cv[31].destination = &(s->log_file_not_found); - cv[32].destination = &(s->log_request_handling); - cv[33].destination = &(s->log_response_header); - cv[34].destination = &(s->log_request_header); - /*cv[35].destination = &(s->unused);*/ - cv[36].destination = &(s->allow_http11); - /*cv[39].destination = s->unused;*/ - - cv[41].destination = &(s->range_requests); - /*cv[47].destination = s->unused;*/ - /*cv[48].destination = &(s->unused);*/ - cv[49].destination = &(s->etag_use_inode); - - cv[50].destination = &(s->etag_use_mtime); - cv[51].destination = &(s->etag_use_size); - cv[53].destination = &(s->log_timeouts); - cv[54].destination = &(s->defer_accept); - /*cv[56].destination = &(s->unused);*/ - /*cv[57].destination = &(s->unused);*/ - /*cv[58].destination = &(s->unused);*/ - /*cv[59].destination = s->unused;*/ - - /*cv[60].destination = &(s->unused);*/ - cv[61].destination = &(s->set_v6only); - /*cv[62].destination = &(s->unused);*/ - /*cv[63].destination = s->unused;*/ - /*cv[64].destination = s->unused;*/ - /*cv[65].destination = &(s->unused);*/ - /*cv[66].destination = &(s->unused);*/ - /*cv[67].destination = &(s->unused);*/ - cv[70].destination = &(s->listen_backlog); - cv[71].destination = s->error_handler_404; - #if defined(__FreeBSD__) || defined(__NetBSD__) \ - || defined(__OpenBSD__) || defined(__DragonFly__) - cv[75].destination = s->bsd_accept_filter; - #endif - cv[76].destination = &(s->stream_request_body); - cv[77].destination = &(s->stream_response_body); - cv[79].destination = &(s->error_intercept); - cv[81].destination = s->socket_perms; - - srv->config_storage[i] = s; - - if (0 != (ret = config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION))) { - break; - } - - if (s->stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN) { - s->stream_request_body |= FDEVENT_STREAM_REQUEST; - } - if (s->stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) { - s->stream_response_body |= FDEVENT_STREAM_RESPONSE; - } - - if (!array_is_kvstring(s->mimetypes)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "unexpected value for mimetype.assign; expected list of \"ext\" => \"mimetype\""); - } - - if (!buffer_string_is_empty(s->server_tag)) { - for (char *t = strchr(s->server_tag->ptr,'\n'); NULL != t; t = strchr(t+2,'\n')) { - /* not expecting admin to define multi-line server.tag, - * but ensure server_tag has proper header continuation, - * if needed */ - off_t off = t - s->server_tag->ptr; - size_t len; - if (t[1] == ' ' || t[1] == '\t') continue; - len = buffer_string_length(s->server_tag); - buffer_string_prepare_append(s->server_tag, 1); - t = s->server_tag->ptr+off; - memmove(t+2, t+1, len - off - 1); - t[1] = ' '; - buffer_commit(s->server_tag, 1); - } - } - - if (0 == i) { - if (!config_http_parseopts(srv, http_parseopts)) { - ret = HANDLER_ERROR; - break; - } +static int config_insert_srvconf(server *srv) { + static const config_plugin_keys_t cpk[] = { + { CONST_STR_LEN("server.modules"), + T_CONFIG_ARRAY, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.compat-module-load"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.systemd-socket-activation"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.port"), + T_CONFIG_SHORT, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.bind"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.network-backend"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.chroot"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.username"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.groupname"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.errorlog"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.breakagelog"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.errorlog-use-syslog"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.syslog-facility"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.core-files"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.event-handler"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.pid-file"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.max-worker"), + T_CONFIG_SHORT, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.max-fds"), + T_CONFIG_SHORT, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.max-connections"), + T_CONFIG_SHORT, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.max-request-field-size"), + T_CONFIG_INT, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.chunkqueue-chunk-sz"), + T_CONFIG_INT, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.upload-temp-file-size"), + T_CONFIG_INT, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.upload-dirs"), + T_CONFIG_ARRAY, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.http-parseopts"), + T_CONFIG_ARRAY, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.http-parseopt-header-strict"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.http-parseopt-host-strict"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.http-parseopt-host-normalize"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.reject-expect-100-with-417"), /*(ignored)*/ + T_CONFIG_BOOL, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("server.stat-cache-engine"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("mimetype.xattr-name"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("ssl.engine"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("debug.log-request-header-on-error"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_SERVER } + ,{ CONST_STR_LEN("debug.log-state-handling"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_SERVER } + ,{ NULL, 0, + T_CONFIG_UNSET, + T_CONFIG_SCOPE_UNSET } + }; + + if (0 != stat_cache_choose_engine(srv, NULL)) /*(set initial default)*/ + return HANDLER_ERROR; + + int rc = 0; + plugin_data_base srvplug; + memset(&srvplug, 0, sizeof(srvplug)); + plugin_data_base * const p = &srvplug; + if (!config_plugin_values_init(srv, p, cpk, "global")) + return HANDLER_ERROR; + + int ssl_enabled = 0; /*(directive checked here only to set default port)*/ + + /* process and validate T_CONFIG_SCOPE_SERVER config directives */ + if (p->cvlist[0].v.u2[1]) { + config_plugin_value_t *cpv = p->cvlist + p->cvlist[0].v.u2[0]; + for (; -1 != cpv->k_id; ++cpv) { + switch (cpv->k_id) { + case 0: /* server.modules */ + if (!array_is_vlist(cpv->v.a)) { + log_error(srv->errh, __FILE__, __LINE__, + "unexpected value for %s; " + "expected list of \"mod_xxxxxx\" strings", + cpk[cpv->k_id].k); + rc = HANDLER_ERROR; + break; } + array_copy_array(srv->srvconf.modules, cpv->v.a); + break; + case 1: /* server.compat-module-load */ + srv->srvconf.compat_module_load = (unsigned short)cpv->v.u; + break; + case 2: /* server.systemd-socket-activation */ + srv->srvconf.systemd_socket_activation=(unsigned short)cpv->v.u; + break; + case 3: /* server.port */ + srv->srvconf.port = cpv->v.shrt; + break; + case 4: /* server.bind */ + srv->srvconf.bindhost = cpv->v.b; + break; + case 5: /* server.network-backend */ + srv->srvconf.network_backend = cpv->v.b; + break; + case 6: /* server.chroot */ + srv->srvconf.changeroot = cpv->v.b; + break; + case 7: /* server.username */ + srv->srvconf.username = cpv->v.b; + break; + case 8: /* server.groupname */ + srv->srvconf.groupname = cpv->v.b; + break; + case 9: /* server.errorlog */ + srv->srvconf.errorlog_file = cpv->v.b; + break; + case 10:/* server.breakagelog */ + srv->srvconf.breakagelog_file = cpv->v.b; + break; + case 11:/* server.errorlog-use-syslog */ + srv->srvconf.errorlog_use_syslog = (unsigned short)cpv->v.u; + break; + case 12:/* server.syslog-facility */ + srv->srvconf.syslog_facility = cpv->v.b; + break; + case 13:/* server.core-files */ + srv->srvconf.enable_cores = (unsigned short)cpv->v.u; + break; + case 14:/* server.event-handler */ + srv->srvconf.event_handler = cpv->v.b->ptr; + break; + case 15:/* server.pid-file */ + *(const buffer **)&srv->srvconf.pid_file = cpv->v.b; + break; + case 16:/* server.max-worker */ + srv->srvconf.max_worker = (unsigned short)cpv->v.u; + break; + case 17:/* server.max-fds */ + srv->srvconf.max_fds = (unsigned short)cpv->v.u; + break; + case 18:/* server.max-connections */ + srv->srvconf.max_conns = (unsigned short)cpv->v.u; + break; + case 19:/* server.max-request-field-size */ + srv->srvconf.max_request_field_size = cpv->v.u; + break; + case 20:/* server.chunkqueue-chunk-sz */ + chunkqueue_set_chunk_size(cpv->v.u); + break; + case 21:/* server.upload-temp-file-size */ + srv->srvconf.upload_temp_file_size = cpv->v.u; + break; + case 22:/* server.upload-dirs */ + if (!array_is_vlist(cpv->v.a)) { + log_error(srv->errh, __FILE__, __LINE__, + "unexpected value for %s; " + "expected list of \"path\" strings", cpk[cpv->k_id].k); + rc = HANDLER_ERROR; + break; + } + array_copy_array(srv->srvconf.upload_tempdirs, cpv->v.a); + break; + case 23:/* server.http-parseopts */ + if (!config_http_parseopts(srv, cpv->v.a)) + rc = HANDLER_ERROR; + break; + case 24:/* server.http-parseopt-header-strict */ + srv->srvconf.http_header_strict = (0 != cpv->v.u); + break; + case 25:/* server.http-parseopt-host-strict */ + srv->srvconf.http_host_strict = (0 != cpv->v.u); + break; + case 26:/* server.http-parseopt-host-normalize */ + srv->srvconf.http_host_normalize = (0 != cpv->v.u); + break; + case 27:/* server.reject-expect-100-with-417 *//*(ignored)*/ + break; + case 28:/* server.stat-cache-engine */ + if (0 != stat_cache_choose_engine(srv, cpv->v.b)) + rc = HANDLER_ERROR; + break; + case 29:/* mimetype.xattr-name */ + srv->srvconf.xattr_name = cpv->v.b->ptr; + break; + case 30:/* ssl.engine */ + ssl_enabled = (0 != cpv->v.u); + #ifndef USE_OPENSSL_CRYPTO + if (ssl_enabled) { + log_error(srv->errh, __FILE__, __LINE__, + "ssl support is missing; " + "recompile with e.g. --with-openssl"); + rc = HANDLER_ERROR; + break; + } + #endif + break; + case 31:/* debug.log-request-header-on-error */ + srv->srvconf.log_request_header_on_error = (0 != cpv->v.u); + break; + case 32:/* debug.log-state-handling */ + srv->srvconf.log_state_handling = (0 != cpv->v.u); + break; + default:/* should not happen */ + break; + } + } + } - if (srv->srvconf.http_url_normalize - && COMP_HTTP_QUERY_STRING == config->comp) { - switch(config->cond) { - case CONFIG_COND_NE: - case CONFIG_COND_EQ: - /* (can use this routine as long as it does not perform - * any regex-specific normalization of first arg) */ - pcre_keyvalue_burl_normalize_key(&config->string, srv->tmp_buf); - break; - case CONFIG_COND_NOMATCH: - case CONFIG_COND_MATCH: - pcre_keyvalue_burl_normalize_key(&config->string, srv->tmp_buf); - if (!data_config_pcre_compile(config)) { - ret = HANDLER_ERROR; - } - break; - default: - break; - } - if (HANDLER_ERROR == ret) break; - } - - #ifndef USE_OPENSSL_CRYPTO - if (s->ssl_enabled) { - log_error_write(srv, __FILE__, __LINE__, "s", - "ssl support is missing, recompile with e.g. --with-openssl"); - ret = HANDLER_ERROR; - break; - } - #endif - } - array_free(http_parseopts); - - { - specific_config *s = srv->config_storage[0]; - s->http_parseopts= /*(global, but stored in con->conf.http_parseopts)*/ - (srv->srvconf.http_header_strict ?(HTTP_PARSEOPT_HEADER_STRICT) :0) - |(srv->srvconf.http_host_strict ?(HTTP_PARSEOPT_HOST_STRICT - |HTTP_PARSEOPT_HOST_NORMALIZE):0) - |(srv->srvconf.http_host_normalize ?(HTTP_PARSEOPT_HOST_NORMALIZE):0) - |(srv->srvconf.http_method_get_body?(HTTP_PARSEOPT_METHOD_GET_BODY):0); - s->http_parseopts |= srv->srvconf.http_url_normalize; - - if (s->log_request_handling || s->log_request_header) - srv->srvconf.log_request_header_on_error = 1; - } - - if (0 != chunk_sz) { - chunkqueue_set_chunk_size(chunk_sz); - } - - if (0 != stat_cache_choose_engine(srv, stat_cache_string)) { - ret = HANDLER_ERROR; - } - buffer_free(stat_cache_string); - - if (!array_is_vlist(srv->srvconf.upload_tempdirs)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "unexpected value for server.upload-dirs; expected list of \"path\" strings"); - ret = HANDLER_ERROR; - } - - if (!array_is_vlist(srv->srvconf.modules)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "unexpected value for server.modules; expected list of \"mod_xxxxxx\" strings"); - ret = HANDLER_ERROR; - } else if (srv->srvconf.compat_module_load) { - data_string *ds; - int prepend_mod_indexfile = 1; - int append_mod_dirlisting = 1; - int append_mod_staticfile = 1; - int append_mod_authn_file = 1; - int append_mod_authn_ldap = 1; - int append_mod_authn_mysql = 1; - int append_mod_openssl = 1; - int contains_mod_auth = 0; - - /* prepend default modules */ - for (i = 0; i < srv->srvconf.modules->used; i++) { - ds = (data_string *)srv->srvconf.modules->data[i]; - - if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("mod_indexfile"))) { - prepend_mod_indexfile = 0; - } - - if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("mod_staticfile"))) { - append_mod_staticfile = 0; - } - - if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("mod_dirlisting"))) { - append_mod_dirlisting = 0; - } - - if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("mod_openssl"))) { - append_mod_openssl = 0; - } - - if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("mod_authn_file"))) { - append_mod_authn_file = 0; - } - - if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("mod_authn_ldap"))) { - append_mod_authn_ldap = 0; - } - - if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("mod_authn_mysql"))) { - append_mod_authn_mysql = 0; - } - - if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("mod_auth"))) { - contains_mod_auth = 1; - } - - if (0 == prepend_mod_indexfile && - 0 == append_mod_dirlisting && - 0 == append_mod_staticfile && - 0 == append_mod_openssl && - 0 == append_mod_authn_file && - 0 == append_mod_authn_ldap && - 0 == append_mod_authn_mysql && - 1 == contains_mod_auth) { - break; - } - } - - if (prepend_mod_indexfile) { - /* mod_indexfile has to be loaded before mod_fastcgi and friends */ - array *modules = array_init(); - array_insert_value(modules, CONST_STR_LEN("mod_indexfile")); - - for (i = 0; i < srv->srvconf.modules->used; i++) { - ds = (data_string *)srv->srvconf.modules->data[i]; - array_insert_value(modules, CONST_BUF_LEN(&ds->value)); - } - - array_free(srv->srvconf.modules); - srv->srvconf.modules = modules; - } - - /* append default modules */ - if (append_mod_dirlisting) { - array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_dirlisting")); - } + if (0 == srv->srvconf.port) + srv->srvconf.port = ssl_enabled ? 443 : 80; - if (append_mod_staticfile) { - array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_staticfile")); - } + if (srv->srvconf.compat_module_load) + config_compat_module_load(srv); - if (append_mod_openssl) { - #ifdef USE_OPENSSL_CRYPTO - config_warn_openssl_module(srv); - #endif - } + if (srv->srvconf.http_url_normalize && !config_burl_normalize_cond(srv)) + rc = HANDLER_ERROR; - /* mod_auth.c,http_auth.c auth backends were split into separate modules - * Automatically load auth backend modules for compatibility with - * existing lighttpd 1.4.x configs */ - if (contains_mod_auth) { - if (append_mod_authn_file) { - array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_authn_file")); - } - if (append_mod_authn_ldap) { - #if defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER) - config_warn_authn_module(srv, CONST_STR_LEN("ldap")); - #endif - } - if (append_mod_authn_mysql) { - #if defined(HAVE_MYSQL) - config_warn_authn_module(srv, CONST_STR_LEN("mysql")); - #endif - } - } - } + free(srvplug.cvlist); + return rc; +} - return ret; +static int config_insert(server *srv) { + static const config_plugin_keys_t cpk[] = { + { CONST_STR_LEN("server.document-root"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.name"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.tag"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.max-request-size"), + T_CONFIG_INT, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.max-keep-alive-requests"), + T_CONFIG_SHORT, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.max-keep-alive-idle"), + T_CONFIG_SHORT, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.max-read-idle"), + T_CONFIG_SHORT, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.max-write-idle"), + T_CONFIG_SHORT, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.errorfile-prefix"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.error-handler"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.error-handler-404"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.error-intercept"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.force-lowercase-filenames"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.follow-symlink"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.protocol-http11"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.range-requests"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.stream-request-body"), + T_CONFIG_SHORT, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.stream-response-body"), + T_CONFIG_SHORT, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.kbytes-per-second"), + T_CONFIG_SHORT, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("connection.kbytes-per-second"), + T_CONFIG_SHORT, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("mimetype.assign"), + T_CONFIG_ARRAY, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("mimetype.use-xattr"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("etag.use-inode"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("etag.use-mtime"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("etag.use-size"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("debug.log-condition-handling"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("debug.log-file-not-found"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("debug.log-request-handling"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("debug.log-request-header"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("debug.log-response-header"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("debug.log-timeouts"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ NULL, 0, + T_CONFIG_UNSET, + T_CONFIG_SCOPE_UNSET } + }; + + if (0 != config_insert_srvconf(srv)) return HANDLER_ERROR; + + int rc = 0; + config_data_base * const p = calloc(1, sizeof(config_data_base)); + force_assert(p); + srv->config_data_base = p; + + if (!config_plugin_values_init(srv, p, cpk, "base")) + return HANDLER_ERROR; + + /* process and validate T_CONFIG_SCOPE_CONNECTION config directives + * (init i to 0 if global context; to 1 to skip empty global context) */ + for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { + config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; + for (; -1 != cpv->k_id; ++cpv) { + switch (cpv->k_id) { + case 0: /* server.document-root */ + case 1: /* server.name */ + break; + case 2: /* server.tag */ + if (!buffer_string_is_empty(cpv->v.b)) { + buffer *b; + *(const buffer **)&b = cpv->v.b; + for (char *t=strchr(b->ptr,'\n'); t; t=strchr(t+2,'\n')) { + /* not expecting admin to define multi-line server.tag, + * but ensure server_tag has proper header continuation, + * if needed */ + if (t[1] == ' ' || t[1] == '\t') continue; + off_t off = t - b->ptr; + size_t len = buffer_string_length(b); + buffer_string_prepare_append(b, 1); + t = b->ptr+off; + memmove(t+2, t+1, len - off - 1); + t[1] = ' '; + buffer_commit(b, 1); + } + } + break; + case 3: /* server.max-request-size */ + case 4: /* server.max-keep-alive-requests */ + case 5: /* server.max-keep-alive-idle */ + case 6: /* server.max-read-idle */ + case 7: /* server.max-write-idle */ + case 8: /* server.errorfile-prefix */ + case 9: /* server.error-handler */ + case 10:/* server.error-handler-404 */ + case 11:/* server.error-intercept */ + break; + case 12:/* server.force-lowercase-filenames */ + #ifndef HAVE_LSTAT + if (0 == cpv->v.u) + log_error(srv->errh, __FILE__, __LINE__, + "Your system lacks lstat(). " + "We can not differ symlinks from files. " + "Please remove server.follow-symlinks from your config."); + #endif + break; + case 13:/* server.follow-symlink */ + case 14:/* server.protocol-http11 */ + case 15:/* server.range-requests */ + break; + case 16:/* server.stream-request-body */ + if (cpv->v.shrt & FDEVENT_STREAM_REQUEST_BUFMIN) + cpv->v.shrt |=FDEVENT_STREAM_REQUEST; + break; + case 17:/* server.stream-response-body */ + if (cpv->v.shrt & FDEVENT_STREAM_RESPONSE_BUFMIN) + cpv->v.shrt |=FDEVENT_STREAM_RESPONSE; + break; + case 18:{/*server.kbytes-per-second */ + off_t * const cnt = malloc(2*sizeof(off_t)); + force_assert(cnt); + cnt[0] = 0; + cnt[1] = (off_t)cpv->v.shrt << 10; + cpv->v.v = cnt; + cpv->vtype = T_CONFIG_LOCAL; + break; + } + case 19:/* connection.kbytes-per-second */ + break; + case 20:/* mimetype.assign */ + if (!array_is_kvstring(cpv->v.a)) { + log_error(srv->errh, __FILE__, __LINE__, + "unexpected value for %s; " + "expected list of \"ext\" => \"mimetype\"", + cpk[cpv->k_id].k); + rc = HANDLER_ERROR; + } + break; + case 21:/* mimetype.use-xattr */ + case 22:/* etag.use-inode */ + case 23:/* etag.use-mtime */ + case 24:/* etag.use-size */ + case 25:/* debug.log-condition-handling */ + case 26:/* debug.log-file-not-found */ + case 27:/* debug.log-request-handling */ + case 28:/* debug.log-request-header */ + case 29:/* debug.log-response-header */ + case 30:/* debug.log-timeouts */ + break; + default:/* should not happen */ + break; + } + } + } -} + p->defaults.max_keep_alive_requests = 100; + p->defaults.max_keep_alive_idle = 5; + p->defaults.max_read_idle = 60; + p->defaults.max_write_idle = 360; + p->defaults.follow_symlink = 1; + p->defaults.allow_http11 = 1; + p->defaults.etag_flags = ETAG_USE_INODE | ETAG_USE_MTIME | ETAG_USE_SIZE; + p->defaults.range_requests = 1; + /* use 2 to detect later if value is set by user config in global section */ + p->defaults.force_lowercase_filenames = 2; + + /*(global, but store in con->conf.http_parseopts)*/ + p->defaults.http_parseopts = + (srv->srvconf.http_header_strict ? HTTP_PARSEOPT_HEADER_STRICT :0) + | (srv->srvconf.http_host_strict ? (HTTP_PARSEOPT_HOST_STRICT + |HTTP_PARSEOPT_HOST_NORMALIZE) :0) + | (srv->srvconf.http_host_normalize ? HTTP_PARSEOPT_HOST_NORMALIZE :0) + | (srv->srvconf.http_method_get_body ? HTTP_PARSEOPT_METHOD_GET_BODY :0); + p->defaults.http_parseopts |= srv->srvconf.http_url_normalize; + p->defaults.mimetypes = &srv->empty_array; /*(mimetypes must not be NULL)*/ + + /* initialize p->defaults from global config context */ + if (p->nconfig > 0 && p->cvlist->v.u2[1]) { + const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; + if (-1 != cpv->k_id) + config_merge_config(&p->defaults, cpv); + } -#define PATCH(x) con->conf.x = s->x -void config_patch_connection(server *srv, connection *con) { - size_t i, j; - - /* skip the first, the global context */ - for (i = 1; i < srv->config_context->used; i++) { - if (!config_check_cond(con, i)) continue; /* condition not matched */ - - data_config *dc = (data_config *)srv->config_context->data[i]; - specific_config *s = srv->config_storage[i]; - - /* merge config */ - for (j = 0; j < dc->value->used; j++) { - data_unset *du = dc->value->data[j]; - - if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.document-root"))) { - PATCH(document_root); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.range-requests"))) { - PATCH(range_requests); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.error-handler"))) { - PATCH(error_handler); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.error-handler-404"))) { - PATCH(error_handler_404); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.error-intercept"))) { - PATCH(error_intercept); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.errorfile-prefix"))) { - PATCH(errorfile_prefix); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("mimetype.assign"))) { - PATCH(mimetypes); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.max-keep-alive-requests"))) { - PATCH(max_keep_alive_requests); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.max-keep-alive-idle"))) { - PATCH(max_keep_alive_idle); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.max-write-idle"))) { - PATCH(max_write_idle); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.max-read-idle"))) { - PATCH(max_read_idle); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.max-request-size"))) { - PATCH(max_request_size); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("mimetype.use-xattr"))) { - PATCH(use_xattr); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("etag.use-inode"))) { - PATCH(etag_use_inode); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("etag.use-mtime"))) { - PATCH(etag_use_mtime); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("etag.use-size"))) { - PATCH(etag_use_size); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.follow-symlink"))) { - PATCH(follow_symlink); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.name"))) { - PATCH(server_name); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.tag"))) { - PATCH(server_tag); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.stream-request-body"))) { - PATCH(stream_request_body); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.stream-response-body"))) { - PATCH(stream_response_body); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("connection.kbytes-per-second"))) { - PATCH(kbytes_per_second); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("debug.log-request-handling"))) { - PATCH(log_request_handling); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("debug.log-request-header"))) { - PATCH(log_request_header); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("debug.log-response-header"))) { - PATCH(log_response_header); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("debug.log-condition-handling"))) { - PATCH(log_condition_handling); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("debug.log-file-not-found"))) { - PATCH(log_file_not_found); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("debug.log-timeouts"))) { - PATCH(log_timeouts); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.protocol-http11"))) { - PATCH(allow_http11); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.force-lowercase-filenames"))) { - PATCH(force_lowercase_filenames); - #if 0 /*(not necessary; used only at startup)*/ - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.listen-backlog"))) { - PATCH(listen_backlog); - #endif - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.kbytes-per-second"))) { - PATCH(global_kbytes_per_second); - PATCH(global_bytes_per_second_cnt); - con->conf.global_bytes_per_second_cnt_ptr = &s->global_bytes_per_second_cnt; - #if 0 /*(not necessary; used only at startup)*/ - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("server.socket-perms"))) { - PATCH(socket_perms); - #endif - } - } - } + /* (after processing config defaults) */ + if (p->defaults.log_request_handling || p->defaults.log_request_header) + srv->srvconf.log_request_header_on_error = 1; - con->etag_flags = (con->conf.etag_use_mtime ? ETAG_USE_MTIME : 0) | - (con->conf.etag_use_inode ? ETAG_USE_INODE : 0) | - (con->conf.etag_use_size ? ETAG_USE_SIZE : 0); + return rc; } -#undef PATCH typedef struct { const char *source; @@ -1442,7 +1657,7 @@ int config_read(server *srv, const char *fn) { int config_set_defaults(server *srv) { size_t i; - specific_config *s = srv->config_storage[0]; + specific_config *s = &((config_data_base *)srv->config_data_base)->defaults; struct stat st1, st2; if (0 != fdevent_config(srv)) return -1; @@ -1496,8 +1711,7 @@ int config_set_defaults(server *srv) { if (buffer_string_is_empty(s->document_root)) { log_error_write(srv, __FILE__, __LINE__, "s", - "a default document-root has to be set"); - + "document-root is not set"); return -1; } @@ -1542,9 +1756,5 @@ int config_set_defaults(server *srv) { } } - if (srv->srvconf.port == 0) { - srv->srvconf.port = s->ssl_enabled ? 443 : 80; - } - return 0; } diff --git a/src/configfile.h b/src/configfile.h index a58c6fe7..b46f4dad 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -128,7 +128,13 @@ int config_parse_file(server *srv, config_t *context, const char *fn); __attribute_cold__ int config_parse_cmd(server *srv, config_t *context, const char *cmd); -void config_patch_connection(server *srv, connection *con); +__attribute_cold__ +void config_free_config(void *p); + +void config_reset_config_bytes_sec(void *p); + +void config_reset_config(server *srv, connection *con); +void config_patch_config(server *srv, connection *con); void config_cond_cache_reset(server *srv, connection *con); void config_cond_cache_reset_item(server *srv, connection *con, comp_key_t item); @@ -150,7 +156,7 @@ typedef enum { T_CONFIG_SCOPE_UNSET, T_CONFIG_SCOPE_CONNECTION } config_scope_type_t; -typedef struct { +typedef struct config_plugin_value { int k_id; config_values_type_t vtype; union v_u { diff --git a/src/connections-glue.c b/src/connections-glue.c index 11c186e4..8e77c97b 100644 --- a/src/connections-glue.c +++ b/src/connections-glue.c @@ -290,8 +290,8 @@ static handler_t connection_handle_read_body_unknown(server *srv, connection *co static off_t connection_write_throttle(server *srv, connection *con, off_t max_bytes) { UNUSED(srv); - if (con->conf.global_kbytes_per_second) { - off_t limit = con->conf.global_kbytes_per_second * 1024 - *(con->conf.global_bytes_per_second_cnt_ptr); + if (con->conf.global_bytes_per_second) { + off_t limit = (off_t)con->conf.global_bytes_per_second - *(con->conf.global_bytes_per_second_cnt_ptr); if (limit <= 0) { /* we reached the global traffic limit */ con->traffic_limit_reached = 1; @@ -302,8 +302,8 @@ static off_t connection_write_throttle(server *srv, connection *con, off_t max_b } } - if (con->conf.kbytes_per_second) { - off_t limit = con->conf.kbytes_per_second * 1024 - con->bytes_written_cur_second; + if (con->conf.bytes_per_second) { + off_t limit = (off_t)con->conf.bytes_per_second - con->bytes_written_cur_second; if (limit <= 0) { /* we reached the traffic limit */ con->traffic_limit_reached = 1; @@ -362,7 +362,8 @@ int connection_write_chunkqueue(server *srv, connection *con, chunkqueue *cq, of written = cq->bytes_out - written; con->bytes_written += written; con->bytes_written_cur_second += written; - *(con->conf.global_bytes_per_second_cnt_ptr) += written; + if (con->conf.global_bytes_per_second_cnt_ptr) + *(con->conf.global_bytes_per_second_cnt_ptr) += written; return ret; } @@ -391,7 +392,8 @@ static int connection_write_100_continue(server *srv, connection *con) { written = cq->bytes_out - written; con->bytes_written += written; con->bytes_written_cur_second += written; - *(con->conf.global_bytes_per_second_cnt_ptr) += written; + if (con->conf.global_bytes_per_second_cnt_ptr) + *(con->conf.global_bytes_per_second_cnt_ptr) += written; if (rc < 0) { con->state = CON_STATE_ERROR; diff --git a/src/connections.c b/src/connections.c index 68e01bb7..e930e026 100644 --- a/src/connections.c +++ b/src/connections.c @@ -535,17 +535,6 @@ static void connection_handle_write_state(server *srv, connection *con) { } -static void connection_reset_config(server *srv, connection *con) { - /* initialize specific_config (con->conf) from top-level specific_config */ - specific_config * const s = srv->config_storage[0]; - const size_t len = /* offsetof() */ - (uintptr_t)&((specific_config *)0)->global_bytes_per_second_cnt_ptr; - con->conf.global_bytes_per_second_cnt_ptr = &s->global_bytes_per_second_cnt; - con->server_name = s->server_name; - memcpy(&con->conf, s, len); -} - - __attribute_cold__ static connection *connection_init(server *srv) { connection *con; @@ -599,7 +588,7 @@ static connection *connection_init(server *srv) { con->cond_cache = calloc(srv->config_context->used, sizeof(cond_cache_t)); force_assert(NULL != con->cond_cache); - connection_reset_config(srv, con); + config_reset_config(srv, con); return con; } @@ -710,7 +699,7 @@ static int connection_reset(server *srv, connection *con) { /*con->error_handler_saved_method = HTTP_METHOD_UNSET;*/ /*(error_handler_saved_method value is not valid unless error_handler_saved_status is set)*/ - connection_reset_config(srv, con); + config_reset_config(srv, con); return 0; } @@ -1177,7 +1166,7 @@ static int connection_handle_request(server *srv, connection *con) { con->error_handler_saved_status = 65535; /* >= 1000 */ } } else if (con->http_status >= 400) { - buffer *error_handler = NULL; + const buffer *error_handler = NULL; if (!buffer_string_is_empty(con->conf.error_handler)) { error_handler = con->conf.error_handler; } else if ((con->http_status == 404 || con->http_status == 403) @@ -1236,7 +1225,7 @@ static int connection_handle_request(server *srv, connection *con) { break; case HANDLER_COMEBACK: if (con->mode == DIRECT && buffer_is_empty(con->physical.path)) { - connection_reset_config(srv, con); + config_reset_config(srv, con); } return 1; case HANDLER_ERROR: @@ -1464,12 +1453,11 @@ static void connection_check_timeout (server * const srv, const time_t cur_ts, c } } - /* we don't like div by zero */ if (0 == (t_diff = cur_ts - con->connection_start)) t_diff = 1; if (con->traffic_limit_reached && - (con->conf.kbytes_per_second == 0 || - ((con->bytes_written / t_diff) < con->conf.kbytes_per_second * 1024))){ + (con->conf.bytes_per_second == 0 || + con->bytes_written < (off_t)con->conf.bytes_per_second * t_diff)) { /* enable connection again */ con->traffic_limit_reached = 0; @@ -1514,8 +1502,8 @@ void connection_graceful_shutdown_maint (server *srv) { con->keep_alive = 0; /* disable keep-alive */ - con->conf.kbytes_per_second = 0; /* disable rate limit */ - con->conf.global_kbytes_per_second = 0; /* disable rate limit */ + con->conf.bytes_per_second = 0; /* disable rate limit */ + con->conf.global_bytes_per_second = 0; /* disable rate limit */ if (con->traffic_limit_reached) { con->traffic_limit_reached = 0; changed = 1; diff --git a/src/etag.c b/src/etag.c index 002b8e0b..0881f48d 100644 --- a/src/etag.c +++ b/src/etag.c @@ -143,7 +143,7 @@ int etag_is_equal(const buffer *etag, const char *line, int weak_ok) { return 0; } -int etag_create(buffer *etag, const struct stat *st, etag_flags_t flags) { +int etag_create(buffer *etag, const struct stat *st, int flags) { if (0 == flags) return 0; buffer_clear(etag); diff --git a/src/etag.h b/src/etag.h index 9460ce1f..35dde1c5 100644 --- a/src/etag.h +++ b/src/etag.h @@ -9,7 +9,7 @@ struct stat; /* declaration */ typedef enum { ETAG_USE_INODE = 1, ETAG_USE_MTIME = 2, ETAG_USE_SIZE = 4 } etag_flags_t; int etag_is_equal(const buffer *etag, const char *matches, int weak_ok); -int etag_create(buffer *etag, const struct stat *st, etag_flags_t flags); +int etag_create(buffer *etag, const struct stat *st, int flags); int etag_mutate(buffer *mut, buffer *etag); diff --git a/src/fdevent.c b/src/fdevent.c index bdea3596..0535b615 100644 --- a/src/fdevent.c +++ b/src/fdevent.c @@ -56,7 +56,7 @@ int fdevent_config(server *srv) { { FDEVENT_HANDLER_UNSET, NULL } }; - if (buffer_string_is_empty(srv->srvconf.event_handler)) { + if (NULL == srv->srvconf.event_handler) { /* choose a good default * * the event_handler list is sorted by 'goodness' @@ -71,24 +71,23 @@ int fdevent_config(server *srv) { return -1; } - buffer_copy_string(srv->srvconf.event_handler, event_handlers[0].name); + srv->srvconf.event_handler = event_handlers[0].name; } else { /* * User override */ for (size_t i = 0; event_handlers[i].name; i++) { - if (0 == strcmp(event_handlers[i].name, srv->srvconf.event_handler->ptr)) { + if (0 == strcmp(event_handlers[i].name, srv->srvconf.event_handler)) { srv->event_handler = event_handlers[i].et; break; } } if (FDEVENT_HANDLER_UNSET == srv->event_handler) { - log_error_write(srv, __FILE__, __LINE__, "sb", - "the selected event-handler in unknown or not supported:", - srv->srvconf.event_handler ); - + log_error(srv->errh, __FILE__, __LINE__, + "the selected event-handler in unknown or not supported: %s", + srv->srvconf.event_handler); return -1; } } @@ -245,8 +244,10 @@ fdevents *fdevent_init(server *srv) { free(ev->fdarray); free(ev); - log_error_write(srv, __FILE__, __LINE__, "sBS", - "event-handler failed:", srv->srvconf.event_handler, "; try to set server.event-handler = \"poll\" or \"select\""); + log_error(srv->errh, __FILE__, __LINE__, + "event-handler failed: %s; " + "try to set server.event-handler = \"poll\" or \"select\"", + srv->srvconf.event_handler); return NULL; } @@ -270,8 +271,11 @@ void fdevent_free(fdevents *ev) { int fdevent_reset(fdevents *ev) { int rc = (NULL != ev->reset) ? ev->reset(ev) : 0; if (-1 == rc) { - log_error_write(ev->srv, __FILE__, __LINE__, "sBS", - "event-handler failed:", ev->srv->srvconf.event_handler, "; try to set server.event-handler = \"poll\" or \"select\""); + const char *event_handler = ev->srv->srvconf.event_handler; + log_error(ev->srv->errh, __FILE__, __LINE__, + "event-handler failed: %s; " + "try to set server.event-handler = \"poll\" or \"select\"", + event_handler ? event_handler : ""); } return rc; } diff --git a/src/http-header-glue.c b/src/http-header-glue.c index 176634a4..fa9ff8c5 100644 --- a/src/http-header-glue.c +++ b/src/http-header-glue.c @@ -510,7 +510,7 @@ void http_response_send_file (server *srv, connection *con, buffer *path) { } if (allow_caching) { - if (con->etag_flags != 0 && !buffer_string_is_empty(stat_cache_etag_get(sce, con->etag_flags))) { + if (con->conf.etag_flags != 0 && !buffer_string_is_empty(stat_cache_etag_get(sce, con->conf.etag_flags))) { if (NULL == http_header_response_get(con, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"))) { /* generate e-tag */ etag_mutate(con->physical.etag, sce->etag); diff --git a/src/mod_compress.c b/src/mod_compress.c index 49ff479f..15087653 100644 --- a/src/mod_compress.c +++ b/src/mod_compress.c @@ -563,7 +563,7 @@ static int deflate_file_to_file(server *srv, connection *con, plugin_data *p, in return 0; } - if (0.0 < p->conf.max_loadavg && p->conf.max_loadavg < srv->srvconf.loadavg[0]) { + if (0.0 < p->conf.max_loadavg && p->conf.max_loadavg < srv->loadavg[0]) { return -1; } @@ -699,7 +699,7 @@ static int deflate_file_to_buffer(server *srv, connection *con, plugin_data *p, if (sce->st.st_size > 128 * 1024 * 1024) return -1; - if (0.0 < p->conf.max_loadavg && p->conf.max_loadavg < srv->srvconf.loadavg[0]) { + if (0.0 < p->conf.max_loadavg && p->conf.max_loadavg < srv->loadavg[0]) { return -1; } @@ -851,7 +851,7 @@ PHYSICALPATH_FUNC(mod_compress_physical) { */ if (sce->st.st_size < 128) return HANDLER_GO_ON; - stat_cache_etag_get(sce, con->etag_flags); + stat_cache_etag_get(sce, con->conf.etag_flags); /* check if mimetype is in compress-config */ content_type = NULL; diff --git a/src/mod_deflate.c b/src/mod_deflate.c index 08cb634f..bddda989 100644 --- a/src/mod_deflate.c +++ b/src/mod_deflate.c @@ -1198,7 +1198,7 @@ CONNECTION_FUNC(mod_deflate_handle_response_start) { } } - if (0.0 < p->conf.max_loadavg && p->conf.max_loadavg < srv->srvconf.loadavg[0]) { + if (0.0 < p->conf.max_loadavg && p->conf.max_loadavg < srv->loadavg[0]) { return HANDLER_GO_ON; } diff --git a/src/mod_dirlisting.c b/src/mod_dirlisting.c index 9d78c4be..af1b9a78 100644 --- a/src/mod_dirlisting.c +++ b/src/mod_dirlisting.c @@ -997,7 +997,7 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf if (con->conf.use_xattr) { memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1); attrlen = sizeof(attrval) - 1; - if (attr_get(path, srv->srvconf.xattr_name->ptr, attrval, &attrlen, 0) == 0) { + if (attr_get(path, srv->srvconf.xattr_name, attrval, &attrlen, 0) == 0) { attrval[attrlen] = '\0'; content_type = attrval; } @@ -1005,7 +1005,7 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf #elif defined(HAVE_EXTATTR) if (con->conf.use_xattr) { memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1); - if(-1 != (attrlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, srv->srvconf.xattr_name->ptr, attrval, sizeof(attrval)-1))) { + if(-1 != (attrlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, srv->srvconf.xattr_name, attrval, sizeof(attrval)-1))) { attrval[attrlen] = '\0'; content_type = attrval; } diff --git a/src/mod_magnet.c b/src/mod_magnet.c index 2c5f55b7..1631cc0c 100644 --- a/src/mod_magnet.c +++ b/src/mod_magnet.c @@ -361,7 +361,7 @@ static int magnet_stat(lua_State *L) { lua_pushinteger(L, sce->st.st_ino); lua_setfield(L, -2, "st_ino"); - if (!buffer_string_is_empty(stat_cache_etag_get(sce, con->etag_flags))) { + if (!buffer_string_is_empty(stat_cache_etag_get(sce, con->conf.etag_flags))) { /* we have to mutate the etag */ etag_mutate(srv->tmp_buf, sce->etag); lua_pushlstring(L, CONST_BUF_LEN(srv->tmp_buf)); diff --git a/src/mod_magnet_cache.c b/src/mod_magnet_cache.c index 760ac8c9..9fddb72c 100644 --- a/src/mod_magnet_cache.c +++ b/src/mod_magnet_cache.c @@ -74,7 +74,7 @@ lua_State *script_cache_get_script(server *srv, connection *con, script_cache *c break; } - stat_cache_etag_get(sce, con->etag_flags); + stat_cache_etag_get(sce, con->conf.etag_flags); if (!buffer_is_equal(sce->etag, sc->etag)) { /* the etag is outdated, reload the function */ lua_pop(sc->L, 1); @@ -115,7 +115,7 @@ lua_State *script_cache_get_script(server *srv, connection *con, script_cache *c } if (HANDLER_GO_ON == stat_cache_get_entry(srv, con, sc->name, &sce)) { - buffer_copy_buffer(sc->etag, stat_cache_etag_get(sce, con->etag_flags)); + buffer_copy_buffer(sc->etag, stat_cache_etag_get(sce, con->conf.etag_flags)); } force_assert(lua_isfunction(sc->L, -1)); diff --git a/src/mod_ssi.c b/src/mod_ssi.c index dc200cf8..eabf512a 100644 --- a/src/mod_ssi.c +++ b/src/mod_ssi.c @@ -1266,7 +1266,7 @@ static int mod_ssi_handle_request(server *srv, connection *con, handler_ctx *p) if (st.st_mtime < include_file_last_mtime) st.st_mtime = include_file_last_mtime; - etag_create(con->physical.etag, &st, con->etag_flags); + etag_create(con->physical.etag, &st, con->conf.etag_flags); etag_mutate(con->physical.etag, con->physical.etag); http_header_response_set(con, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); diff --git a/src/mod_staticfile.c b/src/mod_staticfile.c index cedf43f7..cb5fdc35 100644 --- a/src/mod_staticfile.c +++ b/src/mod_staticfile.c @@ -168,7 +168,7 @@ URIHANDLER_FUNC(mod_staticfile_subrequest) { log_error_write(srv, __FILE__, __LINE__, "s", "-- handling file as static file"); } - if (!p->conf.etags_used) con->etag_flags = 0; + if (!p->conf.etags_used) con->conf.etag_flags = 0; http_response_send_file(srv, con, con->physical.path); return HANDLER_FINISHED; diff --git a/src/mod_status.c b/src/mod_status.c index 11520918..c94e74c5 100644 --- a/src/mod_status.c +++ b/src/mod_status.c @@ -797,7 +797,7 @@ static handler_t mod_status_handle_server_config(server *srv, connection *con) { #endif mod_status_header_append(b, "Network Engine"); - mod_status_row_append(b, "fd-Event-Handler", srv->srvconf.event_handler->ptr); + mod_status_row_append(b, "fd-Event-Handler", srv->srvconf.event_handler); mod_status_header_append(b, "Config-File-Settings"); diff --git a/src/mod_webdav.c b/src/mod_webdav.c index 73753885..658ca494 100644 --- a/src/mod_webdav.c +++ b/src/mod_webdav.c @@ -2129,12 +2129,12 @@ webdav_fcopyfile_sz (int ifd, int ofd, off_t isz) static int webdav_if_match_or_unmodified_since (connection * const con, struct stat *st) { - const buffer *im = (0 != con->etag_flags) + const buffer *im = (0 != con->conf.etag_flags) ? http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("If-Match")) : NULL; - const buffer *inm = (0 != con->etag_flags) + const buffer *inm = (0 != con->conf.etag_flags) ? http_header_request_get(con, HTTP_HEADER_IF_NONE_MATCH, CONST_STR_LEN("If-None-Match")) : NULL; @@ -2151,7 +2151,7 @@ webdav_if_match_or_unmodified_since (connection * const con, struct stat *st) buffer *etagb = con->physical.etag; if (NULL != st && (NULL != im || NULL != inm)) { - etag_create(etagb, st, con->etag_flags); + etag_create(etagb, st, con->conf.etag_flags); etag_mutate(etagb, etagb); } @@ -2187,9 +2187,9 @@ webdav_response_etag (const plugin_config * const pconf, connection * const con, struct stat *st) { server *srv = pconf->srv; - if (0 != con->etag_flags) { + if (0 != con->conf.etag_flags) { buffer *etagb = con->physical.etag; - etag_create(etagb, st, con->etag_flags); + etag_create(etagb, st, con->conf.etag_flags); stat_cache_update_entry(srv,CONST_BUF_LEN(con->physical.path),st,etagb); etag_mutate(etagb, etagb); http_header_response_set(con, HTTP_HEADER_ETAG, @@ -3056,9 +3056,9 @@ webdav_propfind_live_props (const webdav_propfind_bufs * const restrict pb, if (pnum != WEBDAV_PROP_ALL) return 0;/* found *//*(else fall through)*/ __attribute_fallthrough__ case WEBDAV_PROP_GETETAG: - if (0 != pb->con->etag_flags) { + if (0 != pb->con->conf.etag_flags) { buffer *etagb = pb->con->physical.etag; - etag_create(etagb, &pb->st, pb->con->etag_flags); + etag_create(etagb, &pb->st, pb->con->conf.etag_flags); etag_mutate(etagb, etagb); buffer_append_string_len(b, CONST_STR_LEN( "")); @@ -3643,7 +3643,7 @@ webdav_has_lock (connection * const con, if (*p != ']') break; /* invalid syntax */ if (p == etag) continue; /* ignore entity-tag if empty */ if (notflag) continue;/* ignore entity-tag in NOT context */ - if (0 == con->etag_flags) continue; /* ignore entity-tag */ + if (0==con->conf.etag_flags) continue; /*ignore entity-tag*/ struct stat st; if (0 != lstat(con->physical.path->ptr, &st)) { http_status_set_error(con,412);/* Precondition Failed */ @@ -3651,7 +3651,7 @@ webdav_has_lock (connection * const con, } if (S_ISDIR(st.st_mode)) continue;/*we ignore etag if dir*/ buffer *etagb = con->physical.etag; - etag_create(etagb, &st, con->etag_flags); + etag_create(etagb, &st, con->conf.etag_flags); etag_mutate(etagb, etagb); *p = '\0'; int ematch = etag_is_equal(etagb, etag, 0); @@ -4201,7 +4201,7 @@ mod_webdav_put_0 (connection * const con, const plugin_config * const pconf) O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, WEBDAV_FILE_MODE); if (fd >= 0) { - if (0 != con->etag_flags) { + if (0 != con->conf.etag_flags) { /*(skip sending etag if fstat() error; not expected)*/ struct stat st; if (0 == fstat(fd, &st)) webdav_response_etag(pconf, con, &st); @@ -4358,7 +4358,8 @@ mod_webdav_put_linkat_rename (connection * const con, unlink(pathtemp); } - if (0 != con->etag_flags && http_status_get(con) < 300) { /*(201, 204)*/ + if (0 != con->conf.etag_flags + && http_status_get(con) < 300) { /*(201, 204)*/ /*(skip sending etag if fstat() error; not expected)*/ if (0 == fstat(c->file.fd, &st)) webdav_response_etag(pconf, con, &st); @@ -4415,9 +4416,9 @@ mod_webdav_put_deprecated_unsafe_partial_put_compat (connection * const con, mod_webdav_write_cq(con, con->request_content_queue, fd); struct stat st; - if (0 != con->etag_flags && !http_status_is_set(con)) { + if (0 != con->conf.etag_flags && !http_status_is_set(con)) { /*(skip sending etag if fstat() error; not expected)*/ - if (0 != fstat(fd, &st)) con->etag_flags = 0; + if (0 != fstat(fd, &st)) con->conf.etag_flags = 0; } const int wc = close(fd); @@ -4426,7 +4427,7 @@ mod_webdav_put_deprecated_unsafe_partial_put_compat (connection * const con, if (!http_status_is_set(con)) { http_status_set_fin(con, 204); /* No Content */ - if (0 != con->etag_flags) webdav_response_etag(pconf, con, &st); + if (0 != con->conf.etag_flags) webdav_response_etag(pconf, con, &st); } return HANDLER_FINISHED; @@ -4530,9 +4531,9 @@ mod_webdav_put (connection * const con, const plugin_config * const pconf) mod_webdav_write_cq(con, cq, fd); struct stat st; - if (0 != con->etag_flags && !http_status_is_set(con)) { + if (0 != con->conf.etag_flags && !http_status_is_set(con)) { /*(skip sending etag if fstat() error; not expected)*/ - if (0 != fstat(fd, &st)) con->etag_flags = 0; + if (0 != fstat(fd, &st)) con->conf.etag_flags = 0; } const int wc = close(fd); @@ -4547,7 +4548,7 @@ mod_webdav_put (connection * const con, const plugin_config * const pconf) if (201 == http_status_get(con)) webdav_parent_modified(pconf, con->physical.path); if (0 == rename(pathtemp, con->physical.path->ptr)) { - if (0 != con->etag_flags) webdav_response_etag(pconf, con, &st); + if (0 != con->conf.etag_flags) webdav_response_etag(pconf,con,&st); } else { if (errno == EISDIR) @@ -5371,7 +5372,7 @@ mod_webdav_lock (connection * const con, const plugin_config * const pconf) : -1; if (fd >= 0) { /*(skip sending etag if fstat() error; not expected)*/ - if (0 != fstat(fd, &st)) con->etag_flags = 0; + if (0 != fstat(fd, &st)) con->conf.etag_flags = 0; close(fd); created = 1; webdav_parent_modified(pconf, con->physical.path); @@ -5432,7 +5433,7 @@ mod_webdav_lock (connection * const con, const plugin_config * const pconf) CONST_STR_LEN("Lock-Token"), lockstr, sizeof(lockstr)-1); webdav_xml_doc_lock_acquired(con, pconf, &lockdata); - if (0 != con->etag_flags && !S_ISDIR(st.st_mode)) + if (0 != con->conf.etag_flags && !S_ISDIR(st.st_mode)) webdav_response_etag(pconf, con, &st); http_status_set_fin(con, created ? 201 : 200); /* Created | OK */ } diff --git a/src/network.c b/src/network.c index a7e5b4d8..b6cd4454 100644 --- a/src/network.c +++ b/src/network.c @@ -5,6 +5,7 @@ #include "fdevent.h" #include "log.h" #include "connections.h" +#include "plugin.h" #include "configfile.h" #include "sock_addr.h" @@ -137,10 +138,60 @@ static void network_srv_sockets_append(server *srv, server_socket *srv_socket) { srv->srv_sockets.ptr[srv->srv_sockets.used++] = srv_socket; } -static int network_server_init(server *srv, buffer *host_token, size_t sidx, int stdin_fd) { +typedef struct { + /* global or per-socket config; not patched per connection */ + int listen_backlog; + unsigned char ssl_enabled; + unsigned char use_ipv6; + unsigned char set_v6only; /* set_v6only is only a temporary option */ + unsigned char defer_accept; + const buffer *socket_perms; + const buffer *bsd_accept_filter; +} network_socket_config; + +typedef struct { + PLUGIN_DATA; + network_socket_config defaults; + network_socket_config conf; +} network_plugin_data; + +static void network_merge_config_cpv(network_socket_config * const pconf, const config_plugin_value_t * const cpv) { + switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ + case 0: /* ssl.engine */ + pconf->ssl_enabled = (0 != cpv->v.u); + break; + case 1: /* server.listen-backlog */ + pconf->listen_backlog = (int)cpv->v.u; + break; + case 2: /* server.socket-perms */ + pconf->socket_perms = cpv->v.b; + break; + case 3: /* server.bsd-accept-filter */ + pconf->bsd_accept_filter = cpv->v.b; + break; + case 4: /* server.defer-accept */ + pconf->defer_accept = (0 != cpv->v.u); + break; + case 5: /* server.use-ipv6 */ + pconf->use_ipv6 = (0 != cpv->v.u); + break; + case 6: /* server.set-v6only */ + pconf->set_v6only = (0 != cpv->v.u); + break; + default:/* should not happen */ + return; + } +} + +static void network_merge_config(network_socket_config * const pconf, const config_plugin_value_t *cpv) { + do { + network_merge_config_cpv(pconf, cpv); + } while ((++cpv)->k_id != -1); +} + +static int network_server_init(server *srv, network_socket_config *s, buffer *host_token, size_t sidx, int stdin_fd) { server_socket *srv_socket; const char *host; - specific_config *s = srv->config_storage[sidx]; socklen_t addr_len = sizeof(sock_addr); sock_addr addr; int family = 0; @@ -156,7 +207,6 @@ static int network_server_init(server *srv, buffer *host_token, size_t sidx, int * binary addresses are matched further below) */ for (uint32_t i = 0; i < srv->srv_sockets.used; ++i) { if (buffer_is_equal(srv->srv_sockets.ptr[i]->srv_token, host_token)) { - buffer_copy_buffer(host_token, srv->srv_sockets.ptr[i]->srv_token); return 0; } } @@ -398,7 +448,7 @@ int network_close(server *srv) { return 0; } -static int network_socket_activation_nfds(server *srv, int nfds) { +static int network_socket_activation_nfds(server *srv, network_socket_config *s, int nfds) { buffer *host = buffer_init(); socklen_t addr_len; sock_addr addr; @@ -412,7 +462,7 @@ static int network_socket_activation_nfds(server *srv, int nfds) { break; } network_host_normalize_addr_str(host, &addr); - rc = network_server_init(srv, host, 0, fd); + rc = network_server_init(srv, s, host, 0, fd); if (0 != rc) break; srv->srv_sockets.ptr[srv->srv_sockets.used-1]->sidx = (unsigned short)~0u; } @@ -422,13 +472,13 @@ static int network_socket_activation_nfds(server *srv, int nfds) { return rc; } -static int network_socket_activation_from_env(server *srv) { +static int network_socket_activation_from_env(server *srv, network_socket_config *s) { char *listen_pid = getenv("LISTEN_PID"); char *listen_fds = getenv("LISTEN_FDS"); pid_t lpid = listen_pid ? (pid_t)strtoul(listen_pid,NULL,10) : 0; int nfds = listen_fds ? atoi(listen_fds) : 0; int rc = (lpid == getpid() && nfds > 0) - ? network_socket_activation_nfds(srv, nfds) + ? network_socket_activation_nfds(srv, s, nfds) : 0; unsetenv("LISTEN_PID"); unsetenv("LISTEN_FDS"); @@ -438,85 +488,151 @@ static int network_socket_activation_from_env(server *srv) { } int network_init(server *srv, int stdin_fd) { - #ifdef __WIN32 - WSADATA wsaData; - WORD wVersionRequested = MAKEWORD(2, 2); - if (0 != WSAStartup(wVersionRequested, &wsaData)) { - /* Tell the user that we could not find a usable WinSock DLL */ - return -1; - } - #endif + /*(network params used during setup (from $SERVER["socket"] condition))*/ + static const config_plugin_keys_t cpk[] = { + { CONST_STR_LEN("ssl.engine"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.listen-backlog"), + T_CONFIG_INT, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.socket-perms"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.bsd-accept-filter"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.defer-accept"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.use-ipv6"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("server.set-v6only"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ NULL, 0, + T_CONFIG_UNSET, + T_CONFIG_SCOPE_UNSET } + }; + + #ifdef __WIN32 + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 2); + if (0 != WSAStartup(wVersionRequested, &wsaData)) { + /* Tell the user that we could not find a usable WinSock DLL */ + return -1; + } + #endif - if (0 != network_write_init(srv)) return -1; + if (0 != network_write_init(srv)) return -1; - if (srv->srvconf.systemd_socket_activation) { - for (uint32_t i = 0; i < srv->srv_sockets_inherited.used; ++i) { - srv->srv_sockets_inherited.ptr[i]->sidx = (unsigned short)~0u; - } - if (0 != network_socket_activation_from_env(srv)) return -1; - if (0 == srv->srv_sockets_inherited.used) { - srv->srvconf.systemd_socket_activation = 0; - } - } + network_plugin_data np; + memset(&np, 0, sizeof(network_plugin_data)); + network_plugin_data *p = &np; - /* process srv->srvconf.bindhost - * (skip if systemd socket activation is enabled and bindhost is empty; do not additionally listen on "*") */ - if (!srv->srvconf.systemd_socket_activation || !buffer_string_is_empty(srv->srvconf.bindhost)) { - int rc; - buffer *b = buffer_init(); - buffer_copy_buffer(b, srv->srvconf.bindhost); - if (b->ptr[0] != '/') { /*(skip adding port if unix socket path)*/ - buffer_append_string_len(b, CONST_STR_LEN(":")); - buffer_append_int(b, srv->srvconf.port); - } + if (!config_plugin_values_init(srv, p, cpk, "network")) + return HANDLER_ERROR; - rc = (-1 == stdin_fd || 0 == srv->srv_sockets.used) - ? network_server_init(srv, b, 0, stdin_fd) - : close(stdin_fd);/*(graceful restart listening to "/dev/stdin")*/ - buffer_free(b); - if (0 != rc) return -1; - } + p->defaults.listen_backlog = 1024; + p->defaults.defer_accept = 0; + p->defaults.use_ipv6 = 0; + p->defaults.set_v6only = 1; - /* check for $SERVER["socket"] */ - for (uint32_t i = 1; i < srv->config_context->used; ++i) { - config_cond_info cfginfo; - config_get_config_cond_info(srv, i, &cfginfo); - buffer *host_token; - *(const buffer **)&host_token = cfginfo.string; - /*(cfginfo.string is modified during config)*/ - - /* not our stage */ - if (COMP_SERVER_SOCKET != cfginfo.comp) continue; - - if (cfginfo.cond == CONFIG_COND_NE) { - socklen_t addr_len = sizeof(sock_addr); - sock_addr addr; - if (0 != network_host_parse_addr(srv, &addr, &addr_len, host_token, srv->config_storage[i]->use_ipv6)) { - return -1; - } - network_host_normalize_addr_str(host_token, &addr); - continue; - } + /* initialize p->defaults from global config context */ + if (p->nconfig > 0 && p->cvlist->v.u2[1]) { + const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; + if (-1 != cpv->k_id) + network_merge_config(&p->defaults, cpv); + } - if (cfginfo.cond != CONFIG_COND_EQ) continue; + int rc = 0; + do { + + if (srv->srvconf.systemd_socket_activation) { + for (uint32_t i = 0; i < srv->srv_sockets_inherited.used; ++i) { + srv->srv_sockets_inherited.ptr[i]->sidx = (unsigned short)~0u; + } + rc = network_socket_activation_from_env(srv, &p->defaults); + if (0 != rc) break; + if (0 == srv->srv_sockets_inherited.used) { + srv->srvconf.systemd_socket_activation = 0; + } + } - if (0 != network_server_init(srv, host_token, i, -1)) return -1; - } + /* process srv->srvconf.bindhost + * (skip if systemd socket activation is enabled and bindhost is empty; + * do not additionally listen on "*") */ + if (!srv->srvconf.systemd_socket_activation + || !buffer_string_is_empty(srv->srvconf.bindhost)) { + buffer *b = buffer_init(); + buffer_copy_buffer(b, srv->srvconf.bindhost); + if (b->ptr[0] != '/') { /*(skip adding port if unix socket path)*/ + buffer_append_string_len(b, CONST_STR_LEN(":")); + buffer_append_int(b, srv->srvconf.port); + } + + rc = (-1 == stdin_fd || 0 == srv->srv_sockets.used) + ? network_server_init(srv, &p->defaults, b, 0, stdin_fd) + : close(stdin_fd);/*(graceful restart listening to "/dev/stdin")*/ + buffer_free(b); + if (0 != rc) break; + } - if (srv->srvconf.systemd_socket_activation) { - /* activate any inherited sockets not explicitly listed in config file */ - server_socket *srv_socket; - for (uint32_t i = 0; i < srv->srv_sockets_inherited.used; ++i) { - if ((unsigned short)~0u != srv->srv_sockets_inherited.ptr[i]->sidx) continue; - srv->srv_sockets_inherited.ptr[i]->sidx = 0; - srv_socket = calloc(1, sizeof(server_socket)); - force_assert(NULL != srv_socket); - memcpy(srv_socket, srv->srv_sockets_inherited.ptr[i], sizeof(server_socket)); - network_srv_sockets_append(srv, srv_socket); - } - } + /* check for $SERVER["socket"] */ + for (uint32_t i = 1; i < srv->config_context->used; ++i) { + config_cond_info cfginfo; + config_get_config_cond_info(srv, i, &cfginfo); + if (COMP_SERVER_SOCKET != cfginfo.comp) continue;/* not our stage */ + + buffer *host_token; + *(const buffer **)&host_token = cfginfo.string; + /*(cfginfo.string is modified during config)*/ + + memcpy(&p->conf, &p->defaults, sizeof(network_socket_config)); + for (int j = !p->cvlist[0].v.u2[1]; j < p->nconfig; ++j) { + if ((int)i != p->cvlist[j].k_id) continue; + const config_plugin_value_t *cpv = + p->cvlist + p->cvlist[j].v.u2[0]; + network_merge_config(&p->conf, cpv); + break; + } + + if (cfginfo.cond == CONFIG_COND_EQ) { + rc = network_server_init(srv, &p->conf, host_token, i, -1); + if (0 != rc) break; + } + else if (cfginfo.cond == CONFIG_COND_NE) { + socklen_t addr_len = sizeof(sock_addr); + sock_addr addr; + rc = network_host_parse_addr(srv, &addr, &addr_len, + host_token, p->conf.use_ipv6); + if (0 != rc) break; + network_host_normalize_addr_str(host_token, &addr); + } + } + if (0 != rc) break; - return 0; + if (srv->srvconf.systemd_socket_activation) { + /* activate any inherited sockets not explicitly listed in config */ + server_socket *srv_socket; + for (uint32_t i = 0; i < srv->srv_sockets_inherited.used; ++i) { + if ((unsigned short)~0u + != srv->srv_sockets_inherited.ptr[i]->sidx) + continue; + srv->srv_sockets_inherited.ptr[i]->sidx = 0; + srv_socket = calloc(1, sizeof(server_socket)); + force_assert(NULL != srv_socket); + memcpy(srv_socket, srv->srv_sockets_inherited.ptr[i], + sizeof(server_socket)); + network_srv_sockets_append(srv, srv_socket); + } + } + + } while (0); + + free(p->cvlist); + return rc; } void network_unregister_sock(server *srv, server_socket *srv_socket) { diff --git a/src/response.c b/src/response.c index 4328cfc3..84a24b7d 100644 --- a/src/response.c +++ b/src/response.c @@ -35,12 +35,11 @@ static int http_response_omit_header(connection *con, const data_string * const && buffer_eq_icase_ssn(ds->key.ptr+sizeof("X-LIGHTTPD-")-1, CONST_STR_LEN("KBytes-per-second"))) { /* "X-LIGHTTPD-KBytes-per-second" */ - long limit = strtol(ds->value.ptr, NULL, 10); + off_t limit = strtol(ds->value.ptr, NULL, 10) << 10; /*(*=1024)*/ if (limit > 0 - && (limit < con->conf.kbytes_per_second - || 0 == con->conf.kbytes_per_second)) { - if (limit > USHRT_MAX) limit= USHRT_MAX; - con->conf.kbytes_per_second = limit; + && (limit < con->conf.bytes_per_second + || 0 == con->conf.bytes_per_second)) { + con->conf.bytes_per_second = limit; } } return 1; @@ -59,6 +58,7 @@ int http_response_write_header(server *srv, connection *con) { http_status_append(b, con->http_status); /* disable keep-alive if requested */ + if (con->request_count > con->conf.max_keep_alive_requests || 0 == con->conf.max_keep_alive_idle) { con->keep_alive = 0; } else if (0 != con->request.content_length @@ -435,7 +435,7 @@ handler_t http_response_prepare(server *srv, connection *con) { | (1 << COMP_HTTP_URL) | (1 << COMP_HTTP_QUERY_STRING) | (1 << COMP_HTTP_REQUEST_HEADER); - config_patch_connection(srv, con); + config_patch_config(srv, con); /* do we have to downgrade to 1.0 ? */ if (!con->conf.allow_http11) { diff --git a/src/server.c b/src/server.c index 094621a4..7146a8cf 100644 --- a/src/server.c +++ b/src/server.c @@ -87,6 +87,11 @@ /* #define USE_ALARM */ #endif +typedef struct { + PLUGIN_DATA; + specific_config defaults; +} config_data_base; + static int oneshot_fd = 0; static volatile int pid_fd = -2; static server_socket_array graceful_sockets; @@ -234,17 +239,6 @@ static server *server_init(void) { CLEAN(ts_date_str); CLEAN(tmp_buf); - - CLEAN(srvconf.errorlog_file); - CLEAN(srvconf.breakagelog_file); - CLEAN(srvconf.groupname); - CLEAN(srvconf.username); - CLEAN(srvconf.changeroot); - CLEAN(srvconf.bindhost); - CLEAN(srvconf.event_handler); - CLEAN(srvconf.pid_file); - CLEAN(srvconf.syslog_facility); - CLEAN(tmp_chunk_len); #undef CLEAN @@ -269,10 +263,8 @@ static server *server_init(void) { srv->srvconf.modules = array_init(); srv->srvconf.modules_dir = buffer_init_string(LIBRARY_DIR); - srv->srvconf.network_backend = buffer_init(); srv->srvconf.upload_tempdirs = array_init(); - srv->srvconf.reject_expect_100_with_417 = 1; - srv->srvconf.xattr_name = buffer_init_string("Content-Type"); + srv->srvconf.xattr_name = "Content-Type"; srv->srvconf.http_header_strict = 1; srv->srvconf.http_host_strict = 1; /*(implies http_host_normalize)*/ srv->srvconf.http_host_normalize = 0; @@ -283,14 +275,15 @@ static server *server_init(void) { | HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE; srv->srvconf.high_precision_timestamps = 0; srv->srvconf.max_request_field_size = 8192; - srv->srvconf.loadavg[0] = 0.0; - srv->srvconf.loadavg[1] = 0.0; - srv->srvconf.loadavg[2] = 0.0; srv->srvconf.compat_module_load = 1; srv->srvconf.systemd_socket_activation = 0; srv->request_env = plugins_call_handle_request_env; + srv->loadavg[0] = 0.0; + srv->loadavg[1] = 0.0; + srv->loadavg[2] = 0.0; + return srv; } @@ -310,43 +303,14 @@ static void server_free(server *srv) { CLEAN(ts_date_str); CLEAN(tmp_buf); - CLEAN(srvconf.errorlog_file); - CLEAN(srvconf.breakagelog_file); - CLEAN(srvconf.groupname); - CLEAN(srvconf.username); - CLEAN(srvconf.changeroot); - CLEAN(srvconf.bindhost); - CLEAN(srvconf.event_handler); - CLEAN(srvconf.pid_file); CLEAN(srvconf.modules_dir); - CLEAN(srvconf.network_backend); - CLEAN(srvconf.xattr_name); - CLEAN(srvconf.syslog_facility); CLEAN(tmp_chunk_len); #undef CLEAN fdevent_free(srv->ev); - if (srv->config_storage) { - for (uint32_t i = 0; i < srv->config_context->used; ++i) { - specific_config *s = srv->config_storage[i]; - - if (!s) continue; - - buffer_free(s->document_root); - buffer_free(s->server_name); - buffer_free(s->server_tag); - buffer_free(s->error_handler); - buffer_free(s->error_handler_404); - buffer_free(s->errorfile_prefix); - buffer_free(s->socket_perms); - array_free(s->mimetypes); - free(s); - } - free(srv->config_storage); - srv->config_storage = NULL; - } + config_free_config(srv->config_data_base); #define CLEAN(x) \ array_free(srv->x); @@ -918,7 +882,8 @@ static void server_graceful_state (server *srv) { else { server_sockets_close(srv); remove_pid_file(srv); - buffer_clear(srv->srvconf.pid_file); /*(prevent more removal attempts)*/ + /*(prevent more removal attempts)*/ + if (srv->srvconf.pid_file) buffer_clear(srv->srvconf.pid_file); } } @@ -1017,7 +982,7 @@ static int server_main (server * const srv, int argc, char **argv) { while(-1 != (o = getopt(argc, argv, "f:m:i:hvVD1pt"))) { switch(o) { case 'f': - if (srv->config_storage) { + if (srv->config_data_base) { log_error_write(srv, __FILE__, __LINE__, "s", "Can only read one config file. Use the include command to use multiple config files."); return -1; @@ -1055,7 +1020,7 @@ static int server_main (server * const srv, int argc, char **argv) { } #ifdef __CYGWIN__ - if (!srv->config_storage && NULL != getenv("NSSM_SERVICE_NAME")) { + if (!srv->config_data_base && NULL != getenv("NSSM_SERVICE_NAME")) { char *dir = getenv("NSSM_SERVICE_DIR"); if (NULL != dir && 0 != chdir(dir)) { log_error_write(srv, __FILE__, __LINE__, "sss", "chdir failed:", dir, strerror(errno)); @@ -1067,7 +1032,7 @@ static int server_main (server * const srv, int argc, char **argv) { } #endif - if (!srv->config_storage) { + if (!srv->config_data_base) { log_error_write(srv, __FILE__, __LINE__, "s", "No configuration available. Try using -f option."); return -1; @@ -1085,7 +1050,7 @@ static int server_main (server * const srv, int argc, char **argv) { } if (test_config) { - buffer_clear(srv->srvconf.pid_file); + if (srv->srvconf.pid_file) buffer_clear(srv->srvconf.pid_file); if (1 == test_config) { printf("Syntax OK\n"); } else { /*(test_config > 1)*/ @@ -1108,7 +1073,7 @@ static int server_main (server * const srv, int argc, char **argv) { graceful_shutdown = 1; srv->sockets_disabled = 1; srv->srvconf.dont_daemonize = 1; - buffer_clear(srv->srvconf.pid_file); + if (srv->srvconf.pid_file) buffer_clear(srv->srvconf.pid_file); if (srv->srvconf.max_worker) { srv->srvconf.max_worker = 0; log_error_write(srv, __FILE__, __LINE__, "s", @@ -1116,7 +1081,7 @@ static int server_main (server * const srv, int argc, char **argv) { } } - if (buffer_is_equal_string(srv->srvconf.bindhost, CONST_STR_LEN("/dev/stdin"))) { + if (srv->srvconf.bindhost && buffer_is_equal_string(srv->srvconf.bindhost, CONST_STR_LEN("/dev/stdin"))) { stdin_fd = dup(STDIN_FILENO); if (stdin_fd <= STDERR_FILENO) { log_error_write(srv, __FILE__, __LINE__, "s", @@ -1164,13 +1129,6 @@ static int server_main (server * const srv, int argc, char **argv) { return -1; } - /* check document-root */ - if (buffer_string_is_empty(srv->config_storage[0]->document_root)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "document-root is not set\n"); - return -1; - } - if (plugins_load(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "loading plugins finally failed"); @@ -1460,8 +1418,11 @@ static int server_main (server * const srv, int argc, char **argv) { log_error_write(srv, __FILE__, __LINE__, "s", "server started (" PACKAGE_DESC ")"); } - if (buffer_is_empty(srv->config_storage[0]->server_tag)) { - buffer_copy_string_len(srv->config_storage[0]->server_tag, CONST_STR_LEN(PACKAGE_DESC)); + specific_config *s = &((config_data_base *)srv->config_data_base)->defaults; + if (buffer_is_empty(s->server_tag)) { + static const buffer server_tag = + { CONST_STR_LEN(PACKAGE_DESC), 0 }; + s->server_tag = &server_tag; } if (HANDLER_GO_ON != plugins_call_set_defaults(srv)) { @@ -1470,7 +1431,7 @@ static int server_main (server * const srv, int argc, char **argv) { } /* settings might be enabled during module config set defaults */ - srv->config_storage[0]->high_precision_timestamps = srv->srvconf.high_precision_timestamps; + s->high_precision_timestamps = srv->srvconf.high_precision_timestamps; /* dump unused config-keys */ for (i = 0; i < srv->config_context->used; i++) { @@ -1651,7 +1612,7 @@ static int server_main (server * const srv, int argc, char **argv) { close(pid_fd); pid_fd = -1; } - buffer_clear(srv->srvconf.pid_file); + if (srv->srvconf.pid_file) buffer_clear(srv->srvconf.pid_file); fdevent_clr_logger_pipe_pids(); srv->pid = getpid(); @@ -1797,9 +1758,9 @@ static void server_handle_sigalrm (server * const srv, time_t min_ts, time_t las #ifdef HAVE_GETLOADAVG /* refresh loadavg data every 30 seconds */ - if (srv->srvconf.loadts + 30 < min_ts) { - if (-1 != getloadavg(srv->srvconf.loadavg, 3)) { - srv->srvconf.loadts = min_ts; + if (srv->loadts + 30 < min_ts) { + if (-1 != getloadavg(srv->loadavg, 3)) { + srv->loadts = min_ts; } } #endif @@ -1809,9 +1770,7 @@ static void server_handle_sigalrm (server * const srv, time_t min_ts, time_t las /* cleanup stat-cache */ stat_cache_trigger_cleanup(srv); /* reset global/aggregate rate limit counters */ - for (uint32_t i = 0; i < srv->config_context->used; ++i) { - srv->config_storage[i]->global_bytes_per_second_cnt = 0; - } + config_reset_config_bytes_sec(srv->config_data_base); /* if graceful_shutdown, accelerate cleanup of recently completed request/responses */ if (graceful_shutdown && !srv_shutdown) connection_graceful_shutdown_maint(srv); connection_periodic_maint(srv, min_ts); diff --git a/src/stat_cache.c b/src/stat_cache.c index 558651e6..7728e8ab 100644 --- a/src/stat_cache.c +++ b/src/stat_cache.c @@ -610,7 +610,7 @@ int stat_cache_choose_engine (server *srv, const buffer *stat_cache_string) { } #if defined(HAVE_XATTR) -static int stat_cache_attr_get(buffer *buf, char *name, char *xattrname) { +static int stat_cache_attr_get(buffer *buf, char *name, const char *xattrname) { int attrlen; int ret; @@ -622,7 +622,7 @@ static int stat_cache_attr_get(buffer *buf, char *name, char *xattrname) { return ret; } #elif defined(HAVE_EXTATTR) -static int stat_cache_attr_get(buffer *buf, char *name, char *xattrname) { +static int stat_cache_attr_get(buffer *buf, char *name, const char *xattrname) { ssize_t attrlen; buffer_string_prepare_copy(buf, 1023); @@ -695,7 +695,7 @@ const buffer * stat_cache_content_type_get(server *srv, connection *con, const b buffer_clear(sce->content_type); #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) if (con->conf.use_xattr) { - stat_cache_attr_get(sce->content_type, name->ptr, srv->srvconf.xattr_name->ptr); + stat_cache_attr_get(sce->content_type, name->ptr, srv->srvconf.xattr_name); } #else UNUSED(srv); @@ -713,8 +713,8 @@ const buffer * stat_cache_content_type_get(server *srv, connection *con, const b return NULL; } -const buffer * stat_cache_etag_get(stat_cache_entry *sce, etag_flags_t flags) { - /*(invalid caching if user config has multiple, different con->etag_flags +const buffer * stat_cache_etag_get(stat_cache_entry *sce, int flags) { + /*(invalid caching if user cfg has multiple, different con->conf.etag_flags * for same path (not expected, since etag flags should be by filesystem))*/ if (!buffer_string_is_empty(sce->etag)) return sce->etag; diff --git a/src/stat_cache.h b/src/stat_cache.h index 8ff9016b..da0aafba 100644 --- a/src/stat_cache.h +++ b/src/stat_cache.h @@ -38,7 +38,7 @@ void stat_cache_free(struct stat_cache *fc); const buffer * stat_cache_mimetype_by_ext(const connection *con, const char *name, size_t nlen); const buffer * stat_cache_content_type_get(server *srv, connection *con, const buffer *name, stat_cache_entry *sce); -const buffer * stat_cache_etag_get(stat_cache_entry *sce, etag_flags_t flags); +const buffer * stat_cache_etag_get(stat_cache_entry *sce, int flags); void stat_cache_update_entry(server *srv, const char *name, size_t len, struct stat *st, buffer *etagb); void stat_cache_delete_entry(server *srv, const char *name, size_t len); void stat_cache_delete_dir(server *srv, const char *name, size_t len);