1254 lines
44 KiB
C
1254 lines
44 KiB
C
/*
|
|
* Copyright (C) Igor Sysoev
|
|
* Copyright (C) 2007 Manlio Perillo (manlio.perillo@gmail.com)
|
|
* Copyright (c) 2010-2025 Asynchronous 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 <ngx_config.h>
|
|
#include <ngx_core.h>
|
|
#include <ngx_http.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <pwd.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
|
|
#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
|
|
};
|