/* * Copyright (C) Igor Sysoev * Copyright (C) 2007 Manlio Perillo (manlio.perillo@gmail.com) * Copyright (c) 2010-2017 Phusion Holding B.V. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "ngx_http_passenger_module.h" #include "Configuration.h" #include "ContentHandler.h" #include "ConfigGeneral/AutoGeneratedManifestDefaultsInitialization.c" #include "ConfigGeneral/AutoGeneratedSetterFuncs.c" #include "ConfigGeneral/ManifestGeneration.c" #include "MainConfig/AutoGeneratedCreateFunction.c" #include "MainConfig/AutoGeneratedManifestGeneration.c" #include "LocationConfig/AutoGeneratedCreateFunction.c" #include "LocationConfig/AutoGeneratedManifestGeneration.c" #include "cxx_supportlib/Constants.h" #include "cxx_supportlib/vendor-modified/modp_b64.h" static ngx_str_t headers_to_hide[] = { /* NOTE: Do not hide the "Status" header; some broken HTTP clients * expect this header. http://code.google.com/p/phusion-passenger/issues/detail?id=177 */ ngx_string("X-Accel-Expires"), ngx_string("X-Accel-Redirect"), ngx_string("X-Accel-Limit-Rate"), ngx_string("X-Accel-Buffering"), ngx_null_string }; passenger_main_conf_t passenger_main_conf; static ngx_path_init_t ngx_http_passenger_temp_path = { ngx_string(NGX_HTTP_PASSENGER_TEMP_PATH), { 1, 2, 0 } }; static ngx_int_t merge_headers(ngx_conf_t *cf, passenger_loc_conf_t *conf, passenger_loc_conf_t *prev); static ngx_int_t merge_string_array(ngx_conf_t *cf, ngx_array_t **prev, ngx_array_t **conf); static ngx_int_t merge_string_keyval_table(ngx_conf_t *cf, ngx_array_t **prev, ngx_array_t **conf); #include "LocationConfig/AutoGeneratedMergeFunction.c" #include "LocationConfig/AutoGeneratedHeaderSerialization.c" void * passenger_create_main_conf(ngx_conf_t *cf) { passenger_main_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(passenger_main_conf_t)); if (conf == NULL) { return NGX_CONF_ERROR; } conf->default_ruby.data = NULL; conf->default_ruby.len = 0; passenger_create_autogenerated_main_conf(&conf->autogenerated); return conf; } char * passenger_init_main_conf(ngx_conf_t *cf, void *conf_pointer) { passenger_main_conf_t *conf; struct passwd *user_entry; struct group *group_entry; char buf[128]; conf = &passenger_main_conf; *conf = *((passenger_main_conf_t *) conf_pointer); if (conf->autogenerated.abort_on_startup_error == NGX_CONF_UNSET) { conf->autogenerated.abort_on_startup_error = 0; } if (conf->autogenerated.show_version_in_header == NGX_CONF_UNSET) { conf->autogenerated.show_version_in_header = 1; } if (conf->autogenerated.default_user.len == 0) { conf->autogenerated.default_user.len = sizeof(DEFAULT_WEB_APP_USER) - 1; conf->autogenerated.default_user.data = (u_char *) DEFAULT_WEB_APP_USER; } if (conf->autogenerated.default_user.len > sizeof(buf) - 1) { return "Value for 'passenger_default_user' is too long."; } memcpy(buf, conf->autogenerated.default_user.data, conf->autogenerated.default_user.len); buf[conf->autogenerated.default_user.len] = '\0'; user_entry = getpwnam(buf); if (user_entry == NULL) { return "The user specified by the 'passenger_default_user' option does not exist."; } if (conf->autogenerated.default_group.len > 0) { if (conf->autogenerated.default_group.len > sizeof(buf) - 1) { return "Value for 'passenger_default_group' is too long."; } memcpy(buf, conf->autogenerated.default_group.data, conf->autogenerated.default_group.len); buf[conf->autogenerated.default_group.len] = '\0'; group_entry = getgrnam(buf); if (group_entry == NULL) { return "The group specified by the 'passenger_default_group' option does not exist."; } } return NGX_CONF_OK; } void * passenger_create_loc_conf(ngx_conf_t *cf) { passenger_loc_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(passenger_loc_conf_t)); if (conf == NULL) { return NGX_CONF_ERROR; } /* * set by ngx_pcalloc(): * * conf->upstream_config.bufs.num = 0; * conf->upstream_config.next_upstream = 0; * conf->upstream_config.temp_path = NULL; * conf->upstream_config.hide_headers_hash = { NULL, 0 }; * conf->upstream_config.hide_headers = NULL; * conf->upstream_config.pass_headers = NULL; * conf->upstream_config.uri = { 0, NULL }; * conf->upstream_config.location = NULL; * conf->upstream_config.store_lengths = NULL; * conf->upstream_config.store_values = NULL; */ conf->parent = NULL; if (ngx_array_init(&conf->children, cf->pool, 8, sizeof(passenger_loc_conf_t *)) != NGX_OK) { return NGX_CONF_ERROR; } if (cf->conf_file == NULL) { conf->context_source_file.data = (u_char *) NULL; conf->context_source_file.len = 0; conf->context_source_line = 0; } else if (cf->conf_file->file.fd == NGX_INVALID_FILE) { conf->context_source_file.data = (u_char *) "(command line)"; conf->context_source_file.len = sizeof("(command line)") - 1; conf->context_source_line = 0; } else { conf->context_source_file = cf->conf_file->file.name; conf->context_source_line = cf->conf_file->line; } conf->cscf = NULL; conf->clcf = NULL; passenger_create_autogenerated_loc_conf(&conf->autogenerated); /******************************/ /******************************/ conf->upstream_config.pass_headers = NGX_CONF_UNSET_PTR; conf->upstream_config.hide_headers = NGX_CONF_UNSET_PTR; conf->upstream_config.store = NGX_CONF_UNSET; conf->upstream_config.store_access = NGX_CONF_UNSET_UINT; #if NGINX_VERSION_NUM >= 1007005 conf->upstream_config.next_upstream_tries = NGX_CONF_UNSET_UINT; #endif conf->upstream_config.buffering = NGX_CONF_UNSET; conf->upstream_config.request_buffering = NGX_CONF_UNSET; conf->upstream_config.ignore_client_abort = NGX_CONF_UNSET; #if NGINX_VERSION_NUM >= 1007007 conf->upstream_config.force_ranges = NGX_CONF_UNSET; #endif conf->upstream_config.local = NGX_CONF_UNSET_PTR; conf->upstream_config.connect_timeout = NGX_CONF_UNSET_MSEC; conf->upstream_config.send_timeout = NGX_CONF_UNSET_MSEC; conf->upstream_config.read_timeout = NGX_CONF_UNSET_MSEC; #if NGINX_VERSION_NUM >= 1007005 conf->upstream_config.next_upstream_timeout = NGX_CONF_UNSET_MSEC; #endif conf->upstream_config.send_lowat = NGX_CONF_UNSET_SIZE; conf->upstream_config.buffer_size = NGX_CONF_UNSET_SIZE; #if NGINX_VERSION_NUM >= 1027000 conf->upstream_config.limit_rate = NGX_CONF_UNSET_PTR; #elif NGINX_VERSION_NUM >= 1007007 conf->upstream_config.limit_rate = NGX_CONF_UNSET_SIZE; #endif conf->upstream_config.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE; conf->upstream_config.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE; conf->upstream_config.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE; conf->upstream_config.pass_request_headers = NGX_CONF_UNSET; conf->upstream_config.pass_request_body = NGX_CONF_UNSET; #if (NGX_HTTP_CACHE) #if NGINX_VERSION_NUM >= 1007009 conf->upstream_config.cache = NGX_CONF_UNSET; #else conf->upstream_config.cache = NGX_CONF_UNSET_PTR; #endif conf->upstream_config.cache_min_uses = NGX_CONF_UNSET_UINT; conf->upstream_config.cache_bypass = NGX_CONF_UNSET_PTR; conf->upstream_config.no_cache = NGX_CONF_UNSET_PTR; conf->upstream_config.cache_valid = NGX_CONF_UNSET_PTR; conf->upstream_config.cache_lock = NGX_CONF_UNSET; conf->upstream_config.cache_lock_timeout = NGX_CONF_UNSET_MSEC; #if NGINX_VERSION_NUM >= 1007008 conf->upstream_config.cache_lock_age = NGX_CONF_UNSET_MSEC; #endif #if NGINX_VERSION_NUM >= 1006000 conf->upstream_config.cache_revalidate = NGX_CONF_UNSET; #endif #endif conf->upstream_config.intercept_errors = NGX_CONF_UNSET; conf->upstream_config.cyclic_temp_file = 0; conf->upstream_config.change_buffering = 1; ngx_str_set(&conf->upstream_config.module, "passenger"); conf->options_cache.data = NULL; conf->options_cache.len = 0; conf->env_vars_cache.data = NULL; conf->env_vars_cache.len = 0; return conf; } static ngx_int_t serialize_loc_conf_to_headers(ngx_conf_t *cf, passenger_loc_conf_t *conf) { ngx_uint_t i; ngx_keyval_t *env_vars; size_t unencoded_len; u_char *unencoded_buf; if (passenger_serialize_autogenerated_loc_conf_to_headers(cf, conf) == 0) { return NGX_ERROR; } if (conf->autogenerated.env_vars != NULL) { size_t len = 0; u_char *buf; u_char *pos; /* Cache env vars data as base64-serialized string. * First, calculate the length of the unencoded data. */ unencoded_len = 0; env_vars = (ngx_keyval_t *) conf->autogenerated.env_vars->elts; for (i = 0; i < conf->autogenerated.env_vars->nelts; i++) { unencoded_len += env_vars[i].key.len + 1 + env_vars[i].value.len + 1; } /* Create the unecoded data. */ unencoded_buf = pos = (u_char *) malloc(unencoded_len); if (unencoded_buf == NULL) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot allocate buffer of %z bytes for environment variables data", unencoded_len); return NGX_ERROR; } for (i = 0; i < conf->autogenerated.env_vars->nelts; i++) { pos = ngx_copy(pos, env_vars[i].key.data, env_vars[i].key.len); *pos = '\0'; pos++; pos = ngx_copy(pos, env_vars[i].value.data, env_vars[i].value.len); *pos = '\0'; pos++; } assert((size_t) (pos - unencoded_buf) == unencoded_len); /* Create base64-serialized string. */ buf = ngx_palloc(cf->pool, modp_b64_encode_len(unencoded_len)); if (buf == NULL) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot allocate buffer of %z bytes for base64 encoding", modp_b64_encode_len(unencoded_len)); return NGX_ERROR; } len = modp_b64_encode((char *) buf, (const char *) unencoded_buf, unencoded_len); if (len == (size_t) -1) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "error during base64 encoding"); free(unencoded_buf); return NGX_ERROR; } conf->env_vars_cache.data = buf; conf->env_vars_cache.len = len; free(unencoded_buf); } return NGX_OK; } char * passenger_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { passenger_loc_conf_t *prev = parent; passenger_loc_conf_t *conf = child; passenger_loc_conf_t **children_elem; ngx_http_core_loc_conf_t *clcf; size_t size; ngx_hash_init_t hash; clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); /* The following works for all contexts within the http{} block, but does * not work for the http{} block itself. To obtain the ngx_http_core_(loc|srv)_conf_t * associated with the http{} block itself, we also set conf->(cscf|clcf) * from record_loc_conf_source_location(), which is called from the various * configuration setter functions. */ conf->cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module); conf->clcf = clcf; if (passenger_merge_autogenerated_loc_conf(&conf->autogenerated, &prev->autogenerated, cf) == 0) { return NGX_CONF_ERROR; } conf->parent = prev; children_elem = ngx_array_push(&prev->children); if (children_elem == NULL) { ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, "cannot allocate memory"); return NGX_CONF_ERROR; } *children_elem = conf; if (prev->options_cache.data == NULL) { if (serialize_loc_conf_to_headers(cf, prev) != NGX_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot create " PROGRAM_NAME " configuration serialization cache"); return NGX_CONF_ERROR; } } /******************************/ /******************************/ #if (NGX_HTTP_CACHE) && NGINX_VERSION_NUM >= 1007009 if (conf->upstream_config.store > 0) { conf->upstream_config.cache = 0; } if (conf->upstream_config.cache > 0) { conf->upstream_config.store = 0; } #endif #if NGINX_VERSION_NUM >= 1007009 if (conf->upstream_config.store == NGX_CONF_UNSET) { ngx_conf_merge_value(conf->upstream_config.store, prev->upstream_config.store, 0); conf->upstream_config.store_lengths = prev->upstream_config.store_lengths; conf->upstream_config.store_values = prev->upstream_config.store_values; } #else if (conf->upstream_config.store != 0) { ngx_conf_merge_value(conf->upstream_config.store, prev->upstream_config.store, 0); if (conf->upstream_config.store_lengths == NULL) { conf->upstream_config.store_lengths = prev->upstream_config.store_lengths; conf->upstream_config.store_values = prev->upstream_config.store_values; } } #endif ngx_conf_merge_uint_value(conf->upstream_config.store_access, prev->upstream_config.store_access, 0600); #if NGINX_VERSION_NUM >= 1007005 ngx_conf_merge_uint_value(conf->upstream_config.next_upstream_tries, prev->upstream_config.next_upstream_tries, 0); #endif ngx_conf_merge_value(conf->upstream_config.buffering, prev->upstream_config.buffering, 0); ngx_conf_merge_value(conf->upstream_config.request_buffering, prev->upstream_config.request_buffering, 1); ngx_conf_merge_value(conf->upstream_config.ignore_client_abort, prev->upstream_config.ignore_client_abort, 0); #if NGINX_VERSION_NUM >= 1007007 ngx_conf_merge_value(conf->upstream_config.force_ranges, prev->upstream_config.force_ranges, 0); #endif ngx_conf_merge_ptr_value(conf->upstream_config.local, prev->upstream_config.local, NULL); ngx_conf_merge_msec_value(conf->upstream_config.connect_timeout, prev->upstream_config.connect_timeout, 12000000); ngx_conf_merge_msec_value(conf->upstream_config.send_timeout, prev->upstream_config.send_timeout, 12000000); ngx_conf_merge_msec_value(conf->upstream_config.read_timeout, prev->upstream_config.read_timeout, 12000000); #if NGINX_VERSION_NUM >= 1007005 ngx_conf_merge_msec_value(conf->upstream_config.next_upstream_timeout, prev->upstream_config.next_upstream_timeout, 0); #endif ngx_conf_merge_size_value(conf->upstream_config.send_lowat, prev->upstream_config.send_lowat, 0); ngx_conf_merge_size_value(conf->upstream_config.buffer_size, prev->upstream_config.buffer_size, 16 * 1024); #if NGINX_VERSION_NUM >= 1027000 ngx_conf_merge_ptr_value(conf->upstream_config.limit_rate, prev->upstream_config.limit_rate, NULL); #elif NGINX_VERSION_NUM >= 1007007 ngx_conf_merge_size_value(conf->upstream_config.limit_rate, prev->upstream_config.limit_rate, 0); #endif ngx_conf_merge_bufs_value(conf->upstream_config.bufs, prev->upstream_config.bufs, 8, 16 * 1024); if (conf->upstream_config.bufs.num < 2) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "there must be at least 2 \"passenger_buffers\""); return NGX_CONF_ERROR; } size = conf->upstream_config.buffer_size; if (size < conf->upstream_config.bufs.size) { size = conf->upstream_config.bufs.size; } ngx_conf_merge_size_value(conf->upstream_config.busy_buffers_size_conf, prev->upstream_config.busy_buffers_size_conf, NGX_CONF_UNSET_SIZE); if (conf->upstream_config.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) { conf->upstream_config.busy_buffers_size = 2 * size; } else { conf->upstream_config.busy_buffers_size = conf->upstream_config.busy_buffers_size_conf; } if (conf->upstream_config.busy_buffers_size < size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"passenger_busy_buffers_size\" must be equal to or greater " "than the maximum of the value of \"passenger_buffer_size\" and " "one of the \"passenger_buffers\""); return NGX_CONF_ERROR; } if (conf->upstream_config.busy_buffers_size > (conf->upstream_config.bufs.num - 1) * conf->upstream_config.bufs.size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"passenger_busy_buffers_size\" must be less than " "the size of all \"passenger_buffers\" minus one buffer"); return NGX_CONF_ERROR; } ngx_conf_merge_size_value(conf->upstream_config.temp_file_write_size_conf, prev->upstream_config.temp_file_write_size_conf, NGX_CONF_UNSET_SIZE); if (conf->upstream_config.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) { conf->upstream_config.temp_file_write_size = 2 * size; } else { conf->upstream_config.temp_file_write_size = conf->upstream_config.temp_file_write_size_conf; } if (conf->upstream_config.temp_file_write_size < size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"passenger_temp_file_write_size\" must be equal to or greater than " "the maximum of the value of \"passenger_buffer_size\" and " "one of the \"passenger_buffers\""); return NGX_CONF_ERROR; } ngx_conf_merge_size_value(conf->upstream_config.max_temp_file_size_conf, prev->upstream_config.max_temp_file_size_conf, NGX_CONF_UNSET_SIZE); if (conf->upstream_config.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) { conf->upstream_config.max_temp_file_size = 1024 * 1024 * 1024; } else { conf->upstream_config.max_temp_file_size = conf->upstream_config.max_temp_file_size_conf; } if (conf->upstream_config.max_temp_file_size != 0 && conf->upstream_config.max_temp_file_size < size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"passenger_max_temp_file_size\" must be equal to zero to disable " "temporary files usage or must be equal to or greater than " "the maximum of the value of \"passenger_buffer_size\" and " "one of the \"passenger_buffers\""); return NGX_CONF_ERROR; } ngx_conf_merge_bitmask_value(conf->upstream_config.ignore_headers, prev->upstream_config.ignore_headers, NGX_CONF_BITMASK_SET); ngx_conf_merge_bitmask_value(conf->upstream_config.next_upstream, prev->upstream_config.next_upstream, (NGX_CONF_BITMASK_SET |NGX_HTTP_UPSTREAM_FT_ERROR |NGX_HTTP_UPSTREAM_FT_TIMEOUT)); if (conf->upstream_config.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) { conf->upstream_config.next_upstream = NGX_CONF_BITMASK_SET |NGX_HTTP_UPSTREAM_FT_OFF; } if (ngx_conf_merge_path_value(cf, &conf->upstream_config.temp_path, prev->upstream_config.temp_path, &ngx_http_passenger_temp_path) != NGX_OK) { return NGX_CONF_ERROR; } #if (NGX_HTTP_CACHE) #if NGINX_VERSION_NUM >= 1007009 if (conf->upstream_config.cache == NGX_CONF_UNSET) { ngx_conf_merge_value(conf->upstream_config.cache, prev->upstream_config.cache, 0); conf->upstream_config.cache_zone = prev->upstream_config.cache_zone; conf->upstream_config.cache_value = prev->upstream_config.cache_value; } if (conf->upstream_config.cache_zone && conf->upstream_config.cache_zone->data == NULL) { ngx_shm_zone_t *shm_zone; shm_zone = conf->upstream_config.cache_zone; ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"scgi_cache\" zone \"%V\" is unknown", &shm_zone->shm.name); return NGX_CONF_ERROR; } #else ngx_conf_merge_ptr_value(conf->upstream_config.cache, prev->upstream_config.cache, NULL); if (conf->upstream_config.cache && conf->upstream_config.cache->data == NULL) { ngx_shm_zone_t *shm_zone; shm_zone = conf->upstream_config.cache; ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"scgi_cache\" zone \"%V\" is unknown", &shm_zone->shm.name); return NGX_CONF_ERROR; } #endif ngx_conf_merge_uint_value(conf->upstream_config.cache_min_uses, prev->upstream_config.cache_min_uses, 1); ngx_conf_merge_bitmask_value(conf->upstream_config.cache_use_stale, prev->upstream_config.cache_use_stale, (NGX_CONF_BITMASK_SET | NGX_HTTP_UPSTREAM_FT_OFF)); if (conf->upstream_config.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) { conf->upstream_config.cache_use_stale = NGX_CONF_BITMASK_SET | NGX_HTTP_UPSTREAM_FT_OFF; } if (conf->upstream_config.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) { conf->upstream_config.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE; } if (conf->upstream_config.cache_methods == 0) { conf->upstream_config.cache_methods = prev->upstream_config.cache_methods; } conf->upstream_config.cache_methods |= NGX_HTTP_GET | NGX_HTTP_HEAD; ngx_conf_merge_ptr_value(conf->upstream_config.cache_bypass, prev->upstream_config.cache_bypass, NULL); ngx_conf_merge_ptr_value(conf->upstream_config.no_cache, prev->upstream_config.no_cache, NULL); ngx_conf_merge_ptr_value(conf->upstream_config.cache_valid, prev->upstream_config.cache_valid, NULL); if (conf->cache_key.value.data == NULL) { conf->cache_key = prev->cache_key; } ngx_conf_merge_value(conf->upstream_config.cache_lock, prev->upstream_config.cache_lock, 0); ngx_conf_merge_msec_value(conf->upstream_config.cache_lock_timeout, prev->upstream_config.cache_lock_timeout, 5000); ngx_conf_merge_value(conf->upstream_config.cache_revalidate, prev->upstream_config.cache_revalidate, 0); #if NGINX_VERSION_NUM >= 1007008 ngx_conf_merge_msec_value(conf->upstream_config.cache_lock_age, prev->upstream_config.cache_lock_age, 5000); #endif #if NGINX_VERSION_NUM >= 1006000 ngx_conf_merge_value(conf->upstream_config.cache_revalidate, prev->upstream_config.cache_revalidate, 0); #endif #endif ngx_conf_merge_value(conf->upstream_config.pass_request_headers, prev->upstream_config.pass_request_headers, 1); ngx_conf_merge_value(conf->upstream_config.pass_request_body, prev->upstream_config.pass_request_body, 1); ngx_conf_merge_value(conf->upstream_config.intercept_errors, prev->upstream_config.intercept_errors, 0); hash.max_size = 512; hash.bucket_size = ngx_align(64, ngx_cacheline_size); hash.name = "passenger_hide_headers_hash"; if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream_config, &prev->upstream_config, headers_to_hide, &hash) != NGX_OK) { return NGX_CONF_ERROR; } if (conf->upstream_config.upstream == NULL) { conf->upstream_config.upstream = prev->upstream_config.upstream; } if (conf->autogenerated.enabled == 1 /* and not NGX_CONF_UNSET */ && passenger_main_conf.autogenerated.root_dir.len != 0 && clcf->handler == NULL /* no handler set by other modules */) { clcf->handler = passenger_content_handler; } conf->autogenerated.headers_hash_bucket_size = ngx_align( conf->autogenerated.headers_hash_bucket_size, ngx_cacheline_size); hash.max_size = conf->autogenerated.headers_hash_max_size; hash.bucket_size = conf->autogenerated.headers_hash_bucket_size; hash.name = "passenger_headers_hash"; if (merge_headers(cf, conf, prev) != NGX_OK) { return NGX_CONF_ERROR; } if (serialize_loc_conf_to_headers(cf, conf) != NGX_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot create " PROGRAM_NAME " configuration serialization cache"); return NGX_CONF_ERROR; } return NGX_CONF_OK; } static ngx_int_t merge_headers(ngx_conf_t *cf, passenger_loc_conf_t *conf, passenger_loc_conf_t *prev) { u_char *p; size_t size; uintptr_t *code; ngx_uint_t i; ngx_array_t headers_names, headers_merged; ngx_keyval_t *src, *s; ngx_hash_key_t *hk; ngx_hash_init_t hash; ngx_http_script_compile_t sc; ngx_http_script_copy_code_t *copy; if (conf->autogenerated.headers_source == NULL) { conf->flushes = prev->flushes; conf->headers_set_len = prev->headers_set_len; conf->headers_set = prev->headers_set; conf->headers_set_hash = prev->headers_set_hash; conf->autogenerated.headers_source = prev->autogenerated.headers_source; } if (conf->headers_set_hash.buckets #if (NGX_HTTP_CACHE) #if NGINX_VERSION_NUM >= 1007009 && ((conf->upstream_config.cache == NGX_CONF_UNSET) == (prev->upstream_config.cache == NGX_CONF_UNSET)) #else && ((conf->upstream_config.cache == NGX_CONF_UNSET_PTR) == (prev->upstream_config.cache == NGX_CONF_UNSET_PTR)) #endif #endif ) { return NGX_OK; } if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t)) != NGX_OK) { return NGX_ERROR; } if (ngx_array_init(&headers_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t)) != NGX_OK) { return NGX_ERROR; } if (conf->autogenerated.headers_source == NULL) { conf->autogenerated.headers_source = ngx_array_create(cf->pool, 4, sizeof(ngx_keyval_t)); if (conf->autogenerated.headers_source == NULL) { return NGX_ERROR; } } conf->headers_set_len = ngx_array_create(cf->pool, 64, 1); if (conf->headers_set_len == NULL) { return NGX_ERROR; } conf->headers_set = ngx_array_create(cf->pool, 512, 1); if (conf->headers_set == NULL) { return NGX_ERROR; } src = conf->autogenerated.headers_source->elts; for (i = 0; i < conf->autogenerated.headers_source->nelts; i++) { s = ngx_array_push(&headers_merged); if (s == NULL) { return NGX_ERROR; } *s = src[i]; } src = headers_merged.elts; for (i = 0; i < headers_merged.nelts; i++) { hk = ngx_array_push(&headers_names); if (hk == NULL) { return NGX_ERROR; } hk->key = src[i].key; hk->key_hash = ngx_hash_key_lc(src[i].key.data, src[i].key.len); hk->value = (void *) 1; if (src[i].value.len == 0) { continue; } if (ngx_http_script_variables_count(&src[i].value) == 0) { copy = ngx_array_push_n(conf->headers_set_len, sizeof(ngx_http_script_copy_code_t)); if (copy == NULL) { return NGX_ERROR; } copy->code = (ngx_http_script_code_pt) (void *) ngx_http_script_copy_len_code; copy->len = src[i].key.len + sizeof(": ") - 1 + src[i].value.len + sizeof(CRLF) - 1; size = (sizeof(ngx_http_script_copy_code_t) + src[i].key.len + sizeof(": ") - 1 + src[i].value.len + sizeof(CRLF) - 1 + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1); copy = ngx_array_push_n(conf->headers_set, size); if (copy == NULL) { return NGX_ERROR; } copy->code = ngx_http_script_copy_code; copy->len = src[i].key.len + sizeof(": ") - 1 + src[i].value.len + sizeof(CRLF) - 1; p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); p = ngx_cpymem(p, src[i].key.data, src[i].key.len); *p++ = ':'; *p++ = ' '; p = ngx_cpymem(p, src[i].value.data, src[i].value.len); *p++ = CR; *p = LF; } else { copy = ngx_array_push_n(conf->headers_set_len, sizeof(ngx_http_script_copy_code_t)); if (copy == NULL) { return NGX_ERROR; } copy->code = (ngx_http_script_code_pt) (void *) ngx_http_script_copy_len_code; copy->len = src[i].key.len + sizeof(": ") - 1; size = (sizeof(ngx_http_script_copy_code_t) + src[i].key.len + sizeof(": ") - 1 + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1); copy = ngx_array_push_n(conf->headers_set, size); if (copy == NULL) { return NGX_ERROR; } copy->code = ngx_http_script_copy_code; copy->len = src[i].key.len + sizeof(": ") - 1; p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); p = ngx_cpymem(p, src[i].key.data, src[i].key.len); *p++ = ':'; *p = ' '; ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); sc.cf = cf; sc.source = &src[i].value; sc.flushes = &conf->flushes; sc.lengths = &conf->headers_set_len; sc.values = &conf->headers_set; if (ngx_http_script_compile(&sc) != NGX_OK) { return NGX_ERROR; } copy = ngx_array_push_n(conf->headers_set_len, sizeof(ngx_http_script_copy_code_t)); if (copy == NULL) { return NGX_ERROR; } copy->code = (ngx_http_script_code_pt) (void *) ngx_http_script_copy_len_code; copy->len = sizeof(CRLF) - 1; size = (sizeof(ngx_http_script_copy_code_t) + sizeof(CRLF) - 1 + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1); copy = ngx_array_push_n(conf->headers_set, size); if (copy == NULL) { return NGX_ERROR; } copy->code = ngx_http_script_copy_code; copy->len = sizeof(CRLF) - 1; p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); *p++ = CR; *p = LF; } code = ngx_array_push_n(conf->headers_set_len, sizeof(uintptr_t)); if (code == NULL) { return NGX_ERROR; } *code = (uintptr_t) NULL; code = ngx_array_push_n(conf->headers_set, sizeof(uintptr_t)); if (code == NULL) { return NGX_ERROR; } *code = (uintptr_t) NULL; } code = ngx_array_push_n(conf->headers_set_len, sizeof(uintptr_t)); if (code == NULL) { return NGX_ERROR; } *code = (uintptr_t) NULL; hash.hash = &conf->headers_set_hash; hash.key = ngx_hash_key_lc; hash.max_size = conf->autogenerated.headers_hash_max_size; hash.bucket_size = conf->autogenerated.headers_hash_bucket_size; hash.name = "passenger_headers_hash"; hash.pool = cf->pool; hash.temp_pool = NULL; return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts); } static ngx_int_t merge_string_array(ngx_conf_t *cf, ngx_array_t **prev, ngx_array_t **conf) { ngx_str_t *prev_elems, *elem; ngx_uint_t i; if (*prev != NGX_CONF_UNSET_PTR) { if (*conf == NGX_CONF_UNSET_PTR) { *conf = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t)); if (*conf == NULL) { return NGX_ERROR; } } prev_elems = (ngx_str_t *) (*prev)->elts; for (i = 0; i < (*prev)->nelts; i++) { elem = (ngx_str_t *) ngx_array_push(*conf); if (elem == NULL) { return NGX_ERROR; } *elem = prev_elems[i]; } } return NGX_OK; } ngx_int_t passenger_postprocess_config(ngx_conf_t *cf) { ngx_http_conf_ctx_t *http_ctx; passenger_loc_conf_t *toplevel_plcf; ngx_pool_cleanup_t *manifest_cleanup; char *dump_path, *dump_content; FILE *dump_file; u_char *end; http_ctx = cf->ctx; toplevel_plcf = http_ctx->loc_conf[ngx_http_passenger_module.ctx_index]; passenger_main_conf.default_ruby = toplevel_plcf->autogenerated.ruby; if (passenger_main_conf.default_ruby.len == 0) { passenger_main_conf.default_ruby.data = (u_char *) DEFAULT_RUBY; passenger_main_conf.default_ruby.len = strlen(DEFAULT_RUBY); } passenger_main_conf.manifest = generate_config_manifest(cf, toplevel_plcf); manifest_cleanup = ngx_pool_cleanup_add(cf->pool, 0); manifest_cleanup->handler = (ngx_pool_cleanup_pt) psg_json_value_free; manifest_cleanup->data = passenger_main_conf.manifest; if (passenger_main_conf.autogenerated.dump_config_manifest.len != 0) { dump_path = (char *) ngx_pnalloc(cf->temp_pool, passenger_main_conf.autogenerated.dump_config_manifest.len + 1); end = ngx_copy(dump_path, passenger_main_conf.autogenerated.dump_config_manifest.data, passenger_main_conf.autogenerated.dump_config_manifest.len); *end = '\0'; dump_file = fopen(dump_path, "w"); if (dump_file != NULL) { dump_content = psg_json_value_to_styled_string( passenger_main_conf.manifest); ssize_t ret = fwrite(dump_content, 1, strlen(dump_content), dump_file); (void) ret; // Ignore compilation warning. fclose(dump_file); free(dump_content); } else { ngx_conf_log_error(NGX_LOG_ALERT, cf, 0, "Error dumping " PROGRAM_NAME " configuration manifest to %V", &passenger_main_conf.autogenerated.dump_config_manifest); } } return NGX_OK; } static int string_keyval_has_key(ngx_array_t *table, ngx_str_t *key) { ngx_keyval_t *elems; ngx_uint_t i; elems = (ngx_keyval_t *) table->elts; for (i = 0; i < table->nelts; i++) { if (elems[i].key.len == key->len && memcmp(elems[i].key.data, key->data, key->len) == 0) { return 1; } } return 0; } static ngx_int_t merge_string_keyval_table(ngx_conf_t *cf, ngx_array_t **prev, ngx_array_t **conf) { ngx_keyval_t *prev_elems, *elem; ngx_uint_t i; if (*prev != NULL) { if (*conf == NULL) { *conf = ngx_array_create(cf->pool, 4, sizeof(ngx_keyval_t)); if (*conf == NULL) { return NGX_ERROR; } } prev_elems = (ngx_keyval_t *) (*prev)->elts; for (i = 0; i < (*prev)->nelts; i++) { if (!string_keyval_has_key(*conf, &prev_elems[i].key)) { elem = (ngx_keyval_t *) ngx_array_push(*conf); if (elem == NULL) { return NGX_ERROR; } *elem = prev_elems[i]; } } } return NGX_OK; } #ifndef PASSENGER_IS_ENTERPRISE static char * passenger_enterprise_only(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return ": this feature is only available in Phusion Passenger Enterprise. " "You are currently running the open source Phusion Passenger. " "Please learn more about and/or buy Phusion Passenger Enterprise at https://www.phusionpassenger.com/features#premium-features ;"; } #endif static char * passenger_enabled(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { passenger_loc_conf_t *passenger_conf = conf; ngx_http_core_loc_conf_t *clcf; ngx_str_t *value; ngx_url_t upstream_url; passenger_conf->autogenerated.enabled_explicitly_set = 1; record_loc_conf_source_location(cf, passenger_conf, &passenger_conf->autogenerated.enabled_source_file, &passenger_conf->autogenerated.enabled_source_line); value = cf->args->elts; if (ngx_strcasecmp(value[1].data, (u_char *) "on") == 0) { passenger_conf->autogenerated.enabled = 1; /* Register a placeholder value as upstream address. The real upstream * address (the Passenger core socket filename) will be set while processing * requests, because we can't start the watchdog (and thus the Passenger core) * until config loading is done. */ ngx_memzero(&upstream_url, sizeof(ngx_url_t)); upstream_url.url = pp_placeholder_upstream_address; upstream_url.no_resolve = 1; passenger_conf->upstream_config.upstream = ngx_http_upstream_add(cf, &upstream_url, 0); if (passenger_conf->upstream_config.upstream == NULL) { return NGX_CONF_ERROR; } clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = passenger_content_handler; if (clcf->name.data != NULL && clcf->name.data[clcf->name.len - 1] == '/') { clcf->auto_redirect = 1; } } else if (ngx_strcasecmp(value[1].data, (u_char *) "off") == 0) { passenger_conf->autogenerated.enabled = 0; } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"passenger_enabled\" must be either set to \"on\" " "or \"off\""); return NGX_CONF_ERROR; } return NGX_CONF_OK; } static char * passenger_conf_set_request_buffering(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { #ifdef NGINX_NO_SEND_REQUEST_BODY_INFINITE_LOOP_BUG passenger_loc_conf_t *passenger_conf = conf; passenger_conf->autogenerated.upstream_config_request_buffering_explicitly_set = 1; record_loc_conf_source_location(cf, passenger_conf, &passenger_conf->autogenerated.upstream_config_request_buffering_source_file, &passenger_conf->autogenerated.upstream_config_request_buffering_source_line); return ngx_conf_set_flag_slot(cf, cmd, conf); #else return "config cannot be set in Nginx < 1.15.3 due to this bug: https://trac.nginx.org/nginx/ticket/1618"; #endif /* NGINX_NO_SEND_REQUEST_BODY_INFINITE_LOOP_BUG */ } static char * rails_framework_spawner_idle_time(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_conf_log_error(NGX_LOG_ALERT, cf, 0, "The 'rails_framework_spawner_idle_time' " "directive is deprecated; please set 'passenger_max_preloader_idle_time' instead"); return NGX_CONF_OK; } static char * passenger_use_global_queue(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_conf_log_error(NGX_LOG_ALERT, cf, 0, "The 'passenger_use_global_queue' " "directive is obsolete and doesn't do anything anymore. Global queuing " "is now always enabled. Please remove this configuration directive."); return NGX_CONF_OK; } static char * passenger_obsolete_directive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_conf_log_error(NGX_LOG_ALERT, cf, 0, "The '%V' directive is obsolete " "and doesn't do anything anymore.", &cmd->name); return NGX_CONF_OK; } PsgJsonValue * psg_json_value_set_str_array(PsgJsonValue *doc, const char *name, ngx_array_t *ary) { PsgJsonValue *subdoc = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_ARRAY); PsgJsonValue *elem, *result; ngx_str_t *values; ngx_uint_t i; if (ary != NULL) { values = (ngx_str_t *) ary->elts; for (i = 0; i < ary->nelts; i++) { elem = psg_json_value_new_str( (const char *) values[i].data, values[i].len); psg_json_value_append_val(subdoc, elem); psg_json_value_free(elem); } } result = psg_json_value_set_value(doc, name, -1, subdoc); psg_json_value_free(subdoc); return result; } PsgJsonValue * psg_json_value_set_str_keyval(PsgJsonValue *doc, const char *name, ngx_array_t *ary) { PsgJsonValue *subdoc = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_OBJECT); PsgJsonValue *elem, *result; ngx_keyval_t *values; ngx_uint_t i; if (ary != NULL) { values = (ngx_keyval_t *) ary->elts; for (i = 0; i < ary->nelts; i++) { elem = psg_json_value_new_str( (const char *) values[i].value.data, values[i].value.len); psg_json_value_set_value(subdoc, (const char *) values[i].key.data, values[i].key.len, elem); psg_json_value_free(elem); } } result = psg_json_value_set_value(doc, name, -1, subdoc); psg_json_value_free(subdoc); return result; } const ngx_command_t passenger_commands[] = { #include "ConfigGeneral/AutoGeneratedDefinitions.c" ngx_null_command };