/* *** uWSGI/mod_Ruwsgi *** Copyright 2009-2010 Roger Florkowski Copyright 2009-2010 Unbit S.a.s. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #define MOD_UWSGI_VERSION "1.0" #include "ap_config.h" #include "apr_version.h" #include "apr_lib.h" #include "apr_strings.h" #include "httpd.h" #include "http_core.h" #include "http_request.h" #include "http_config.h" #include "http_log.h" #include "http_protocol.h" #include "util_script.h" #include #include #include #include #include #include #define DEFAULT_TIMEOUT 60 /* default socket timeout */ #define DEFAULT_CGIMODE 0 /* not enabled */ #define UNSET 0 #define ENABLED 1 #define DISABLED 2 #if APR_MAJOR_VERSION == 0 #define apr_socket_send apr_send #define GET_PORT(port, addr) apr_sockaddr_port_get(&(port), addr) #define CREATE_SOCKET(sock, family, pool) \ apr_socket_create(sock, family, SOCK_STREAM, pool) #else #define GET_PORT(port, addr) ((port) = (addr)->port) #define CREATE_SOCKET(sock, family, pool) \ apr_socket_create(sock, family, SOCK_STREAM, APR_PROTO_TCP, pool) #endif typedef struct { char *path; char *addr; apr_port_t port; } mount_entry; /* * Configuration record. Used per-directory configuration data. */ typedef struct { mount_entry mount; int enabled; /* mod_uwsgi is enabled from this directory */ int timeout; int cgi_mode; uint8_t modifier1; uint8_t modifier2; } uwsgi_cfg; /* Server level configuration */ typedef struct { apr_array_header_t *mounts; int timeout; int cgi_mode; } uwsgi_server_cfg; module AP_MODULE_DECLARE_DATA uwsgi_module; /* * Locate our directory configuration record for the current request. */ static uwsgi_cfg * our_dconfig(request_rec *r) { return (uwsgi_cfg *) ap_get_module_config(r->per_dir_config, &uwsgi_module); } static uwsgi_server_cfg *our_sconfig(server_rec *s) { return (uwsgi_server_cfg *) ap_get_module_config(s->module_config, &uwsgi_module); } static int mount_entry_matches(const char *url, const char *prefix, const char **path_info) { int i; for (i=0; prefix[i] != '\0'; i++) { if (url[i] == '\0' || url[i] != prefix[i]) return 0; } if (url[i] == '\0' || url[i] == '/') { *path_info = url + i; return 1; } return 0; } static int uwsgi_translate(request_rec *r) { uwsgi_cfg *cfg = our_dconfig(r); if (cfg->enabled == DISABLED) { return DECLINED; } if (cfg->mount.addr != UNSET) { ap_assert(cfg->mount.port != UNSET); r->handler = "uwsgi-handler"; r->filename = r->uri; return OK; } else { int i; uwsgi_server_cfg *scfg = our_sconfig(r->server); mount_entry *entries = (mount_entry *) scfg->mounts->elts; for (i = 0; i < scfg->mounts->nelts; ++i) { const char *path_info; mount_entry *mount = &entries[i]; if (mount_entry_matches(r->uri, mount->path, &path_info)) { r->handler = "uwsgi-handler"; r->path_info = apr_pstrdup(r->pool, path_info); r->filename = r->uri; ap_set_module_config(r->request_config, &uwsgi_module, mount); return OK; } } } return DECLINED; } static int uwsgi_map_location(request_rec *r) { if (r->handler && strcmp(r->handler, "uwsgi-handler") == 0) { return OK; /* We don't want directory walk. */ } return DECLINED; } #if AP_MODULE_MAGIC_AT_LEAST(20111130,0) static void log_err(const char *file, int line, int ami, request_rec *r, apr_status_t status, const char *msg) { ap_log_rerror(file, line, APLOG_MODULE_INDEX, APLOG_ERR, status, r, "uwsgi: %s", msg); #else static void log_err(const char *file, int line, request_rec *r, apr_status_t status, const char *msg) { ap_log_rerror(file, line, APLOG_ERR, status, r, "uwsgi: %s", msg); #endif } #if AP_MODULE_MAGIC_AT_LEAST(20111130,0) static void log_debug(const char *file, int line, int ami, request_rec *r, const char *msg) { /* ap_log_rerror(file, line, APLOG_DEBUG, APR_SUCCESS, r, msg); ap_log_rerror(file, line, APLOG_WARNING, APR_SUCCESS, r, "uwsgi: %s", msg); */ ap_log_rerror(file, line, APLOG_MODULE_INDEX, APLOG_DEBUG, APR_SUCCESS, r, "uwsgi: %s", msg); #else static void log_debug(const char *file, int line, request_rec *r, const char *msg) { ap_log_rerror(file, line, APLOG_DEBUG, APR_SUCCESS, r, "uwsgi: %s", msg); #endif } /* buffered socket implementation (buckets are overkill) */ #define BUFFER_SIZE 8000 struct sockbuff { apr_socket_t *sock; char buf[BUFFER_SIZE]; int used; }; static void binit(struct sockbuff *s, apr_socket_t *sock) { s->sock = sock; s->used = 0; } static apr_status_t sendall(apr_socket_t *sock, char *buf, apr_size_t len) { apr_status_t rv; apr_size_t n; while (len > 0) { n = len; if ((rv = apr_socket_send(sock, buf, &n))) return rv; buf += n; len -= n; } return APR_SUCCESS; } static apr_status_t bflush(struct sockbuff *s) { apr_status_t rv; ap_assert(s->used >= 0 && s->used <= BUFFER_SIZE); if (s->used) { if ((rv = sendall(s->sock, s->buf, s->used))) return rv; s->used = 0; } return APR_SUCCESS; } static apr_status_t bwrite(struct sockbuff *s, char *buf, apr_size_t len) { apr_status_t rv; if (len >= BUFFER_SIZE - s->used) { if ((rv = bflush(s))) return rv; while (len >= BUFFER_SIZE) { if ((rv = sendall(s->sock, buf, BUFFER_SIZE))) return rv; buf += BUFFER_SIZE; len -= BUFFER_SIZE; } } if (len > 0) { ap_assert(len < BUFFER_SIZE - s->used); memcpy(s->buf + s->used, buf, len); s->used += len; } return APR_SUCCESS; } static apr_status_t bputs(struct sockbuff *s, char *buf) { return bwrite(s, buf, strlen(buf)); } static apr_status_t bputc(struct sockbuff *s, char c) { char buf[1]; buf[0] = c; return bwrite(s, buf, 1); } static apr_status_t bputh(struct sockbuff *s, unsigned short h) { return bwrite(s, (char *)&h, 2); } #define CONFIG_VALUE(value, fallback) ((value) != UNSET ? (value) : (fallback)) static apr_status_t open_socket(apr_socket_t **sock, request_rec *r) { int timeout; int retries = 4; int sleeptime = 1; apr_status_t rv; apr_sockaddr_t *sockaddr; uwsgi_server_cfg *scfg = our_sconfig(r->server); uwsgi_cfg *cfg = our_dconfig(r); mount_entry *m = (mount_entry *) ap_get_module_config(r->request_config, &uwsgi_module); if (!m) { m = &cfg->mount; } timeout = CONFIG_VALUE(cfg->timeout, CONFIG_VALUE(scfg->timeout, DEFAULT_TIMEOUT)); rv = apr_sockaddr_info_get(&sockaddr, CONFIG_VALUE(m->addr, "localhost"), APR_UNSPEC, CONFIG_VALUE(m->port, 5000), 0, r->pool); if (rv) { log_err(APLOG_MARK, r, rv, "apr_sockaddr_info_get() error"); return rv; } restart: *sock = NULL; rv = CREATE_SOCKET(sock, sockaddr->family, r->pool); if (rv) { log_err(APLOG_MARK, r, rv, "apr_socket_create() error"); return rv; } rv = apr_socket_timeout_set(*sock, apr_time_from_sec(timeout)); if (rv) { log_err(APLOG_MARK, r, rv, "apr_socket_timeout_set() error"); return rv; } rv = apr_socket_connect(*sock, sockaddr); if (rv) { apr_socket_close(*sock); if ((APR_STATUS_IS_ECONNREFUSED(rv) | APR_STATUS_IS_EINPROGRESS(rv)) && retries > 0) { /* server may be temporarily down, retry */ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, rv, r, "uwsgi: connection failed, retrying"); apr_sleep(apr_time_from_sec(sleeptime)); --retries; sleeptime *= 2; goto restart; } log_err(APLOG_MARK, r, rv, "uwsgi: can't connect to server"); return rv; } #ifdef APR_TCP_NODELAY /* disable Nagle, we don't send small packets */ apr_socket_opt_set(*sock, APR_TCP_NODELAY, 1); #endif return APR_SUCCESS; } /* This code is a duplicate of what's in util_script.c. We can't use * r->unparsed_uri because it gets changed if there was a redirect. */ static char *original_uri(request_rec *r) { char *first, *last; if (r->the_request == NULL) { return (char *) apr_pcalloc(r->pool, 1); } first = r->the_request; /* use the request-line */ while (*first && !apr_isspace(*first)) { ++first; /* skip over the method */ } while (apr_isspace(*first)) { ++first; /* and the space(s) */ } last = first; while (*last && !apr_isspace(*last)) { ++last; /* end at next whitespace */ } return apr_pstrmemdup(r->pool, first, last - first); } static char *lookup_name(apr_table_t *t, const char *name) { const apr_array_header_t *hdrs_arr = apr_table_elts(t); apr_table_entry_t *hdrs = (apr_table_entry_t *) hdrs_arr->elts; int i; for (i = 0; i < hdrs_arr->nelts; ++i) { if (hdrs[i].key == NULL) continue; if (strcasecmp(hdrs[i].key, name) == 0) return hdrs[i].val; } return NULL; } static char *lookup_header(request_rec *r, const char *name) { return lookup_name(r->headers_in, name); } static void add_header(apr_table_t *t, const char *name, const char *value) { if (name != NULL && value != NULL) apr_table_addn(t, name, value); } static int find_path_info(const char *uri, const char *path_info) { int n; n = strlen(uri) - strlen(path_info); ap_assert(n >= 0); return n; } static char *http2env(apr_pool_t *p, const char *name) { char *env_name = apr_pstrcat(p, "HTTP_", name, NULL); char *cp; for (cp = env_name + 5; *cp != 0; cp++) { if (*cp == '-') { *cp = '_'; } else { *cp = apr_toupper(*cp); } } return env_name; } static apr_status_t send_headers(request_rec *r, struct sockbuff *s) { int i; const apr_array_header_t *hdrs_arr, *env_arr; apr_table_entry_t *hdrs, *env; char *buf; unsigned short int n = 0; apr_table_t *t; apr_status_t rv = 0; apr_port_t port = 0; #if AP_MODULE_MAGIC_AT_LEAST(20111130,0) GET_PORT(port, r->useragent_addr); #else GET_PORT(port, r->connection->remote_addr); #endif uwsgi_cfg *cfg = our_dconfig(r); log_debug(APLOG_MARK,r, "sending headers"); t = apr_table_make(r->pool, 40); if (!t) return APR_ENOMEM; /* headers to send */ /* CONTENT_LENGTH must come first and always be present */ buf = lookup_header(r, "Content-Length"); if (buf == NULL) buf = "0"; add_header(t, "CONTENT_LENGTH", buf); add_header(t, "REQUEST_METHOD", (char *) r->method); add_header(t, "QUERY_STRING", r->args ? r->args : ""); #if AP_MODULE_MAGIC_AT_LEAST(20111130,0) add_header(t, "SERVER_SOFTWARE", ap_get_server_description()); #else add_header(t, "SERVER_SOFTWARE", ap_get_server_version()); #endif add_header(t, "SERVER_ADMIN", r->server->server_admin); add_header(t, "SERVER_NAME", (char *) ap_get_server_name(r)); add_header(t, "SERVER_PORT", apr_psprintf(r->pool, "%u",ap_get_server_port(r))); add_header(t, "SERVER_ADDR", r->connection->local_ip); add_header(t, "SERVER_PROTOCOL", r->protocol); add_header(t, "REQUEST_URI", original_uri(r)); #if AP_MODULE_MAGIC_AT_LEAST(20111130,0) add_header(t, "REMOTE_ADDR", r->useragent_ip); #else add_header(t, "REMOTE_ADDR", r->connection->remote_ip); #endif add_header(t, "REMOTE_PORT", apr_psprintf(r->pool, "%d", port)); add_header(t, "REMOTE_USER", r->user); add_header(t, "DOCUMENT_ROOT", (char *) ap_document_root(r)); if (r->path_info) { int path_info_start = find_path_info(r->uri, r->path_info); add_header(t, "SCRIPT_NAME", apr_pstrndup(r->pool, r->uri, path_info_start)); add_header(t, "PATH_INFO", r->path_info); } else { /* skip PATH_INFO, don't know it */ add_header(t, "SCRIPT_NAME", r->uri); } add_header(t, "CONTENT_TYPE", lookup_header(r, "Content-type")); /* HTTP headers */ hdrs_arr = apr_table_elts(r->headers_in); hdrs = (apr_table_entry_t *) hdrs_arr->elts; for (i = 0; i < hdrs_arr->nelts; ++i) { if (hdrs[i].key) { add_header(t, http2env(r->pool, hdrs[i].key), hdrs[i].val); } } /* environment variables */ env_arr = apr_table_elts(r->subprocess_env); env = (apr_table_entry_t*) env_arr->elts; for (i = 0; i < env_arr->nelts; ++i) { add_header(t, env[i].key, env[i].val); } hdrs_arr = apr_table_elts(t); hdrs = (apr_table_entry_t*) hdrs_arr->elts; /* calculate length of header data */ for (i = 0; i < hdrs_arr->nelts; ++i) { n += strlen(hdrs[i].key); n += strlen(hdrs[i].val); n += 4; /* XXX */ } log_debug(APLOG_MARK,r, apr_psprintf(r->pool, "pktheader size is %u", n)); log_debug(APLOG_MARK,r, apr_psprintf(r->pool, "num headers %u", hdrs_arr->nelts)); /* write pkt header */ rv = bputc(s, cfg->modifier1); /* marker */ if (rv) return rv; rv = bputh(s, n); /* length of header data */ if (rv) return rv; rv = bputc(s, cfg->modifier2); /* marker */ if (rv) return rv; /* write out headers */ for (i = 0; i < hdrs_arr->nelts; ++i) { rv = bputh(s, strlen(hdrs[i].key)); /* length */ if (rv) return rv; rv = bputs(s, hdrs[i].key); /* key */ if (rv) return rv; rv = bputh(s, strlen(hdrs[i].val)); /* length */ if (rv) return rv; rv = bputs(s, hdrs[i].val); /* data */ if (rv) return rv; } return APR_SUCCESS; } static apr_status_t send_request_body(request_rec *r, struct sockbuff *s) { if (ap_should_client_block(r)) { char buf[BUFFER_SIZE]; apr_status_t rv; apr_off_t len; while ((len = ap_get_client_block(r, buf, sizeof buf)) > 0) { if ((rv = bwrite(s, buf, len))) return rv; } if (len == -1) return HTTP_INTERNAL_SERVER_ERROR; /* what to return? */ } return APR_SUCCESS; } static int uwsgi_handler(request_rec *r) { apr_status_t rv = 0; int cgi_mode, http_status = 0; struct sockbuff s; apr_socket_t *sock; apr_bucket_brigade *bb = NULL; apr_bucket *b = NULL; const char *location; uwsgi_cfg *cfg = our_dconfig(r); uwsgi_server_cfg *scfg = our_sconfig(r->server); if (strcmp(r->handler, "uwsgi-handler")) return DECLINED; http_status = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR); if (http_status != OK) return http_status; log_debug(APLOG_MARK, r, "connecting to server"); rv = open_socket(&sock, r); if (rv) { return HTTP_INTERNAL_SERVER_ERROR; } binit(&s, sock); rv = send_headers(r, &s); if (rv) { log_err(APLOG_MARK, r, rv, "error sending request headers"); return HTTP_INTERNAL_SERVER_ERROR; } rv = send_request_body(r, &s); if (rv) { log_err(APLOG_MARK, r, rv, "error sending request body"); return HTTP_INTERNAL_SERVER_ERROR; } rv = bflush(&s); if (rv) { log_err(APLOG_MARK, r, rv, "error sending request"); return HTTP_INTERNAL_SERVER_ERROR; } log_debug(APLOG_MARK, r, "reading response headers"); bb = apr_brigade_create(r->connection->pool, r->connection->bucket_alloc); cgi_mode = CONFIG_VALUE(cfg->cgi_mode, CONFIG_VALUE(scfg->cgi_mode, DEFAULT_CGIMODE)); if (!cgi_mode) { /* receive implemented with http-0.9 based protocol */ apr_interval_time_t timeout; char buf[4096]; apr_pollfd_t pollfd = {r->pool, APR_POLL_SOCKET, APR_POLLIN, 0, { NULL }, NULL }; pollfd.desc.s = sock; timeout = CONFIG_VALUE(cfg->timeout, CONFIG_VALUE(scfg->timeout, DEFAULT_TIMEOUT)) * 1000 * 1000; r->assbackwards = 1; /* XXX */ for(;;) { apr_int32_t nsds; apr_size_t len; if ((rv = apr_poll(&pollfd, 1, &nsds, timeout)) == APR_TIMEUP) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "uwsgi: recv() timeout"); break; } if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "uwsgi: poll() %s", strerror(errno)); break; } len = 4096; rv = apr_socket_recv(sock, buf, &len); if (rv == APR_SUCCESS) { apr_brigade_write(bb, NULL, NULL, buf, len); } else if (rv == APR_EOF) { break; } else { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "uwsgi: recv() %s", strerror(errno)); } } log_debug(APLOG_MARK,r, "recv finished"); b = apr_bucket_flush_create(r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); b = apr_bucket_eos_create(r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); } else { /* receive implemented with http-1.x based protocol */ b = apr_bucket_socket_create(sock, r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); b = apr_bucket_eos_create(r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); rv = ap_scan_script_header_err_brigade(r, bb, NULL); if (rv) { if (rv == HTTP_INTERNAL_SERVER_ERROR) { log_err(APLOG_MARK, r, rv, "error reading response headers"); } else { /* Work around an Apache bug whereby the returned status is * ignored and status_line is used instead. This bug is * present at least in 2.0.54. */ r->status_line = NULL; } apr_brigade_destroy(bb); return rv; } location = apr_table_get(r->headers_out, "Location"); if (location && location[0] == '/' && ((r->status == HTTP_OK) || ap_is_HTTP_REDIRECT(r->status))) { apr_brigade_destroy(bb); /* Internal redirect -- fake-up a pseudo-request */ r->status = HTTP_OK; /* This redirect needs to be a GET no matter what the original * method was. */ r->method = apr_pstrdup(r->pool, "GET"); r->method_number = M_GET; ap_internal_redirect_handler(location, r); return OK; } } rv = ap_pass_brigade(r->output_filters, bb); if (rv) { log_err(APLOG_MARK, r, rv, "ap_pass_brigade()"); return HTTP_INTERNAL_SERVER_ERROR; } return OK; } static int uwsgi_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *base_server) { ap_add_version_component(p, "mod_uwsgi/" MOD_UWSGI_VERSION); return OK; } static void * uwsgi_create_dir_config(apr_pool_t *p, char *dirspec) { uwsgi_cfg *cfg = apr_pcalloc(p, sizeof(uwsgi_cfg)); cfg->enabled = UNSET; cfg->mount.addr = UNSET; cfg->mount.port = UNSET; cfg->timeout = UNSET; cfg->cgi_mode = UNSET; cfg->modifier1 = UNSET; cfg->modifier2 = UNSET; return cfg; } #define MERGE(b, n, a) (n->a == UNSET ? b->a : n->a) static void * uwsgi_merge_dir_config(apr_pool_t *p, void *basev, void *newv) { uwsgi_cfg* cfg = apr_pcalloc(p, sizeof(uwsgi_cfg)); uwsgi_cfg* base = basev; uwsgi_cfg* new = newv; cfg->enabled = MERGE(base, new, enabled); cfg->mount.addr = MERGE(base, new, mount.addr); cfg->mount.port = MERGE(base, new, mount.port); cfg->timeout = MERGE(base, new, timeout); cfg->cgi_mode = MERGE(base, new, cgi_mode); cfg->modifier1 = MERGE(base, new, modifier1); cfg->modifier2 = MERGE(base, new, modifier2); return cfg; } static void * uwsgi_create_server_config(apr_pool_t *p, server_rec *s) { uwsgi_server_cfg *c = (uwsgi_server_cfg *) apr_pcalloc(p, sizeof(uwsgi_server_cfg)); c->mounts = apr_array_make(p, 20, sizeof(mount_entry)); c->timeout = UNSET; c->cgi_mode = UNSET; return c; } static void * uwsgi_merge_server_config(apr_pool_t *p, void *basev, void *overridesv) { uwsgi_server_cfg *c = (uwsgi_server_cfg *) apr_pcalloc(p, sizeof(uwsgi_server_cfg)); uwsgi_server_cfg *base = (uwsgi_server_cfg *) basev; uwsgi_server_cfg *overrides = (uwsgi_server_cfg *) overridesv; c->mounts = apr_array_append(p, overrides->mounts, base->mounts); c->timeout = MERGE(base, overrides, timeout); c->cgi_mode = MERGE(base, overrides, cgi_mode); return c; } static const char * cmd_uwsgi_mount(cmd_parms *cmd, void *dummy, const char *path, const char *addr) { int n; apr_status_t rv; char *scope_id = NULL; /* A ip6 parameter - not used here. */ uwsgi_server_cfg *scfg = our_sconfig(cmd->server); mount_entry *new = apr_array_push(scfg->mounts); n = strlen(path); while (n > 0 && path[n-1] == '/') { n--; /* strip trailing slashes */ } new->path = apr_pstrndup(cmd->pool, path, n); rv = apr_parse_addr_port(&new->addr, &scope_id, &new->port, addr, cmd->pool); if (rv) return "error parsing address:port string"; return NULL; } static const char * cmd_uwsgi_server(cmd_parms *cmd, void *pcfg, const char *addr_and_port) { apr_status_t rv; uwsgi_cfg *cfg = pcfg; char *scope_id = NULL; /* A ip6 parameter - not used here. */ if (cmd->path == NULL) return "not a server command"; rv = apr_parse_addr_port(&cfg->mount.addr, &scope_id, &cfg->mount.port, addr_and_port, cmd->pool); if (rv) return "error parsing address:port string"; return NULL; } static const char * cmd_uwsgi_handler(cmd_parms* cmd, void* pcfg, int flag) { uwsgi_cfg *cfg = pcfg; if (cmd->path == NULL) /* server command */ return "not a server command"; if (flag) cfg->enabled = ENABLED; else cfg->enabled = DISABLED; return NULL; } static const char * cmd_uwsgi_timeout(cmd_parms *cmd, void* pcfg, const char *strtimeout) { uwsgi_cfg *dcfg = pcfg; int timeout = atoi(strtimeout); if (cmd->path == NULL) { uwsgi_server_cfg *scfg = our_sconfig(cmd->server); scfg->timeout = timeout; } else { dcfg->timeout = timeout; } return NULL; } static const char * cmd_uwsgi_force_cgi_mode(cmd_parms *cmd, void *pcfg, const char *value) { int cgi_mode; if (!strcmp("yes", value) || !strcmp("on", value) || !strcmp("enable", value) || !strcmp("1", value)) { cgi_mode = 1; } else { cgi_mode = 0; } if (cmd->path == NULL) { uwsgi_server_cfg *scfg = our_sconfig(cmd->server); scfg->cgi_mode = cgi_mode; } else { uwsgi_cfg *dcfg = pcfg; dcfg->cgi_mode = cgi_mode; } return NULL; } static const char * cmd_uwsgi_modifier1(cmd_parms *cmd, void *pcfg, const char *value) { uwsgi_cfg *cfg = pcfg; int val; if (cmd->path == NULL) /* server command */ return "not a server command"; val = atoi(value); if (val < 0 || val > 255) { return "ignored uWSGImodifier1. Value must be between 0 and 255"; } else { cfg->modifier1 = (uint8_t) val; } return NULL; } static const char * cmd_uwsgi_modifier2(cmd_parms *cmd, void *pcfg, const char *value) { uwsgi_cfg *cfg = pcfg; int val; if (cmd->path == NULL) /* server command */ return "not a server command"; val = atoi(value); if (val < 0 || val > 255) { return "ignored uWSGImodifier2. Value must be between 0 and 255"; } else { cfg->modifier2 = (uint8_t) val; } return NULL; } static const command_rec uwsgi_cmds[] = { AP_INIT_TAKE2("uWSGImount", cmd_uwsgi_mount, NULL, RSRC_CONF, "path prefix and address of UWSGI server"), AP_INIT_TAKE1("uWSGIserver", cmd_uwsgi_server, NULL, ACCESS_CONF, "Address and port of an UWSGI server (e.g. localhost:4000)"), AP_INIT_FLAG( "uWSGIhandler", cmd_uwsgi_handler, NULL, ACCESS_CONF, "On or Off to enable or disable the UWSGI handler"), AP_INIT_TAKE1("uWSGIserverTimeout", cmd_uwsgi_timeout, NULL, ACCESS_CONF|RSRC_CONF, "Timeout (in seconds) for communication with the UWSGI server."), AP_INIT_TAKE1("uWSGIforceCGImode", cmd_uwsgi_force_cgi_mode, NULL, ACCESS_CONF|RSRC_CONF, "Force uWSGI CGI mode for perfect integration with apache filter"), AP_INIT_TAKE1("uWSGImodifier1", cmd_uwsgi_modifier1, NULL, ACCESS_CONF, "Set uWSGI modifier1"), AP_INIT_TAKE1("uWSGImodifier2", cmd_uwsgi_modifier2, NULL, ACCESS_CONF, "Set uWSGI modifier2"), /* AP_INIT_TAKE12("uWSGIsocket", cmd_uwsgi_socket, NULL, RSRC_CONF|ACCESS_CONF, "Absolute path and optional timeout in seconds of uwsgi server socket"), AP_INIT_TAKE1("uWSGIsocket2", cmd_uwsgi_socket2, NULL, RSRC_CONF|ACCESS_CONF, "Absolute path of failover uwsgi server socket"), AP_INIT_TAKE1("uWSGIforceScriptName", cmd_uwsgi_force_script_name, NULL, ACCESS_CONF, "Fix for PATH_INFO/SCRIPT_NAME when the location has filesystem correspondence"), */ {NULL} }; static void uwsgi_register_hooks(apr_pool_t *p) { ap_hook_post_config(uwsgi_init, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_handler(uwsgi_handler, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_translate_name(uwsgi_translate, NULL, NULL, APR_HOOK_LAST); ap_hook_map_to_storage(uwsgi_map_location, NULL, NULL, APR_HOOK_FIRST); } /* Dispatch list for API hooks */ module AP_MODULE_DECLARE_DATA uwsgi_module = { STANDARD20_MODULE_STUFF, uwsgi_create_dir_config, /* create per-dir config structs */ uwsgi_merge_dir_config, /* merge per-dir config structs */ uwsgi_create_server_config, /* create per-server config structs */ uwsgi_merge_server_config, /* merge per-server config structs */ uwsgi_cmds, /* table of config file commands */ uwsgi_register_hooks, /* register hooks */ };