Blame modules/proxy/mod_proxy_scgi.c

Packit 90a5c9
/* Licensed to the Apache Software Foundation (ASF) under one or more
Packit 90a5c9
 * contributor license agreements.  See the NOTICE file distributed with
Packit 90a5c9
 * this work for additional information regarding copyright ownership.
Packit 90a5c9
 * The ASF licenses this file to You under the Apache License, Version 2.0
Packit 90a5c9
 * (the "License"); you may not use this file except in compliance with
Packit 90a5c9
 * the License.  You may obtain a copy of the License at
Packit 90a5c9
 *
Packit 90a5c9
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 90a5c9
 *
Packit 90a5c9
 * Unless required by applicable law or agreed to in writing, software
Packit 90a5c9
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 90a5c9
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 90a5c9
 * See the License for the specific language governing permissions and
Packit 90a5c9
 * limitations under the License.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * mod_proxy_scgi.c
Packit 90a5c9
 * Proxy backend module for the SCGI protocol
Packit 90a5c9
 * (http://python.ca/scgi/protocol.txt)
Packit 90a5c9
 *
Packit 90a5c9
 * Andr� Malo (nd/perlig.de), August 2007
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
#define APR_WANT_MEMFUNC
Packit 90a5c9
#define APR_WANT_STRFUNC
Packit 90a5c9
#include "apr_strings.h"
Packit 90a5c9
#include "ap_hooks.h"
Packit 90a5c9
#include "apr_optional_hooks.h"
Packit 90a5c9
#include "apr_buckets.h"
Packit 90a5c9
Packit 90a5c9
#include "httpd.h"
Packit 90a5c9
#include "http_config.h"
Packit 90a5c9
#include "http_log.h"
Packit 90a5c9
#include "http_protocol.h"
Packit 90a5c9
#include "http_request.h"
Packit 90a5c9
#include "util_script.h"
Packit 90a5c9
Packit 90a5c9
#include "mod_proxy.h"
Packit 90a5c9
#include "scgi.h"
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
#define SCHEME "scgi"
Packit 90a5c9
#define PROXY_FUNCTION "SCGI"
Packit 90a5c9
#define SCGI_MAGIC "SCGI"
Packit 90a5c9
#define SCGI_PROTOCOL_VERSION "1"
Packit 90a5c9
Packit 90a5c9
/* just protect from typos */
Packit 90a5c9
#define CONTENT_LENGTH "CONTENT_LENGTH"
Packit 90a5c9
#define GATEWAY_INTERFACE "GATEWAY_INTERFACE"
Packit 90a5c9
Packit 90a5c9
module AP_MODULE_DECLARE_DATA proxy_scgi_module;
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
typedef enum {
Packit 90a5c9
    scgi_internal_redirect,
Packit 90a5c9
    scgi_sendfile
Packit 90a5c9
} scgi_request_type;
Packit 90a5c9
Packit 90a5c9
typedef struct {
Packit 90a5c9
    const char *location;    /* target URL */
Packit 90a5c9
    scgi_request_type type;  /* type of request */
Packit 90a5c9
} scgi_request_config;
Packit 90a5c9
Packit 90a5c9
const char *scgi_sendfile_off = "off";
Packit 90a5c9
const char *scgi_sendfile_on = "X-Sendfile";
Packit 90a5c9
const char *scgi_internal_redirect_off = "off";
Packit 90a5c9
const char *scgi_internal_redirect_on = "Location";
Packit 90a5c9
Packit 90a5c9
typedef struct {
Packit 90a5c9
    const char *sendfile;
Packit 90a5c9
    const char *internal_redirect;
Packit 90a5c9
} scgi_config;
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * We create our own bucket type, which is actually derived (c&p) from the
Packit 90a5c9
 * socket bucket.
Packit 90a5c9
 * Maybe some time this should be made more abstract (like passing an
Packit 90a5c9
 * interception function to read or something) and go into the ap_ or
Packit 90a5c9
 * even apr_ namespace.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
typedef struct {
Packit 90a5c9
    apr_socket_t *sock;
Packit 90a5c9
    apr_off_t *counter;
Packit 90a5c9
} socket_ex_data;
Packit 90a5c9
Packit 90a5c9
static apr_bucket *bucket_socket_ex_create(socket_ex_data *data,
Packit 90a5c9
                                           apr_bucket_alloc_t *list);
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
static apr_status_t bucket_socket_ex_read(apr_bucket *a, const char **str,
Packit 90a5c9
                                          apr_size_t *len,
Packit 90a5c9
                                          apr_read_type_e block)
Packit 90a5c9
{
Packit 90a5c9
    socket_ex_data *data = a->data;
Packit 90a5c9
    apr_socket_t *p = data->sock;
Packit 90a5c9
    char *buf;
Packit 90a5c9
    apr_status_t rv;
Packit 90a5c9
    apr_interval_time_t timeout;
Packit 90a5c9
Packit 90a5c9
    if (block == APR_NONBLOCK_READ) {
Packit 90a5c9
        apr_socket_timeout_get(p, &timeout);
Packit 90a5c9
        apr_socket_timeout_set(p, 0);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    *str = NULL;
Packit 90a5c9
    *len = APR_BUCKET_BUFF_SIZE;
Packit 90a5c9
    buf = apr_bucket_alloc(*len, a->list);
Packit 90a5c9
Packit 90a5c9
    rv = apr_socket_recv(p, buf, len);
Packit 90a5c9
Packit 90a5c9
    if (block == APR_NONBLOCK_READ) {
Packit 90a5c9
        apr_socket_timeout_set(p, timeout);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (rv != APR_SUCCESS && rv != APR_EOF) {
Packit 90a5c9
        apr_bucket_free(buf);
Packit 90a5c9
        return rv;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (*len > 0) {
Packit 90a5c9
        apr_bucket_heap *h;
Packit 90a5c9
Packit 90a5c9
        /* count for stats */
Packit 90a5c9
        *data->counter += *len;
Packit 90a5c9
Packit 90a5c9
        /* Change the current bucket to refer to what we read */
Packit 90a5c9
        a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free);
Packit 90a5c9
        h = a->data;
Packit 90a5c9
        h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */
Packit 90a5c9
        *str = buf;
Packit 90a5c9
        APR_BUCKET_INSERT_AFTER(a, bucket_socket_ex_create(data, a->list));
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        apr_bucket_free(buf);
Packit 90a5c9
        a = apr_bucket_immortal_make(a, "", 0);
Packit 90a5c9
        *str = a->data;
Packit 90a5c9
    }
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const apr_bucket_type_t bucket_type_socket_ex = {
Packit 90a5c9
    "SOCKET_EX", 5, APR_BUCKET_DATA,
Packit 90a5c9
    apr_bucket_destroy_noop,
Packit 90a5c9
    bucket_socket_ex_read,
Packit 90a5c9
    apr_bucket_setaside_notimpl,
Packit 90a5c9
    apr_bucket_split_notimpl,
Packit 90a5c9
    apr_bucket_copy_notimpl
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
static apr_bucket *bucket_socket_ex_make(apr_bucket *b, socket_ex_data *data)
Packit 90a5c9
{
Packit 90a5c9
    b->type        = &bucket_type_socket_ex;
Packit 90a5c9
    b->length      = (apr_size_t)(-1);
Packit 90a5c9
    b->start       = -1;
Packit 90a5c9
    b->data        = data;
Packit 90a5c9
    return b;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_bucket *bucket_socket_ex_create(socket_ex_data *data,
Packit 90a5c9
                                           apr_bucket_alloc_t *list)
Packit 90a5c9
{
Packit 90a5c9
    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
Packit 90a5c9
Packit 90a5c9
    APR_BUCKET_INIT(b);
Packit 90a5c9
    b->free = apr_bucket_free;
Packit 90a5c9
    b->list = list;
Packit 90a5c9
    return bucket_socket_ex_make(b, data);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Canonicalize scgi-like URLs.
Packit 90a5c9
 */
Packit 90a5c9
static int scgi_canon(request_rec *r, char *url)
Packit 90a5c9
{
Packit 90a5c9
    char *host, sport[sizeof(":65535")];
Packit 90a5c9
    const char *err, *path;
Packit 90a5c9
    apr_port_t port, def_port;
Packit 90a5c9
Packit 90a5c9
    if (strncasecmp(url, SCHEME "://", sizeof(SCHEME) + 2)) {
Packit 90a5c9
        return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
    url += sizeof(SCHEME); /* Keep slashes */
Packit 90a5c9
Packit 90a5c9
    port = def_port = SCGI_DEF_PORT;
Packit 90a5c9
Packit 90a5c9
    err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
Packit 90a5c9
    if (err) {
Packit 90a5c9
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00857)
Packit 90a5c9
                      "error parsing URL %s: %s", url, err);
Packit 90a5c9
        return HTTP_BAD_REQUEST;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (port != def_port) {
Packit 90a5c9
        apr_snprintf(sport, sizeof(sport), ":%u", port);
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        sport[0] = '\0';
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (ap_strchr(host, ':')) { /* if literal IPv6 address */
Packit 90a5c9
        host = apr_pstrcat(r->pool, "[", host, "]", NULL);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
Packit 90a5c9
                             r->proxyreq);
Packit 90a5c9
    if (!path) {
Packit 90a5c9
        return HTTP_BAD_REQUEST;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    r->filename = apr_pstrcat(r->pool, "proxy:" SCHEME "://", host, sport, "/",
Packit 90a5c9
                              path, NULL);
Packit 90a5c9
Packit 90a5c9
    if (apr_table_get(r->subprocess_env, "proxy-scgi-pathinfo")) {
Packit 90a5c9
        r->path_info = apr_pstrcat(r->pool, "/", path, NULL);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return OK;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Send a block of data, ensure, everything is sent
Packit 90a5c9
 */
Packit 90a5c9
static int sendall(proxy_conn_rec *conn, const char *buf, apr_size_t length,
Packit 90a5c9
                   request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    apr_status_t rv;
Packit 90a5c9
    apr_size_t written;
Packit 90a5c9
Packit 90a5c9
    while (length > 0) {
Packit 90a5c9
        written = length;
Packit 90a5c9
        if ((rv = apr_socket_send(conn->sock, buf, &written)) != APR_SUCCESS) {
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00858)
Packit 90a5c9
                          "sending data to %s:%u failed",
Packit 90a5c9
                          conn->hostname, conn->port);
Packit 90a5c9
            return HTTP_SERVICE_UNAVAILABLE;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        /* count for stats */
Packit 90a5c9
        conn->worker->s->transferred += written;
Packit 90a5c9
        buf += written;
Packit 90a5c9
        length -= written;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return OK;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Send SCGI header block
Packit 90a5c9
 */
Packit 90a5c9
static int send_headers(request_rec *r, proxy_conn_rec *conn)
Packit 90a5c9
{
Packit 90a5c9
    char *buf, *cp, *bodylen;
Packit 90a5c9
    const char *ns_len;
Packit 90a5c9
    const apr_array_header_t *env_table;
Packit 90a5c9
    const apr_table_entry_t *env;
Packit 90a5c9
    int j;
Packit 90a5c9
    apr_size_t len, bodylen_size;
Packit 90a5c9
    apr_size_t headerlen =   sizeof(CONTENT_LENGTH)
Packit 90a5c9
                           + sizeof(SCGI_MAGIC)
Packit 90a5c9
                           + sizeof(SCGI_PROTOCOL_VERSION);
Packit 90a5c9
Packit 90a5c9
    ap_add_common_vars(r);
Packit 90a5c9
    ap_add_cgi_vars(r);
Packit 90a5c9
Packit 90a5c9
    /*
Packit 90a5c9
     * The header blob basically takes the environment and concatenates
Packit 90a5c9
     * keys and values using 0 bytes. There are special treatments here:
Packit 90a5c9
     *   - GATEWAY_INTERFACE and SCGI_MAGIC are dropped
Packit 90a5c9
     *   - CONTENT_LENGTH is always set and must be sent as the very first
Packit 90a5c9
     *     variable
Packit 90a5c9
     *
Packit 90a5c9
     * Additionally it's wrapped into a so-called netstring (see SCGI spec)
Packit 90a5c9
     */
Packit 90a5c9
    env_table = apr_table_elts(r->subprocess_env);
Packit 90a5c9
    env = (apr_table_entry_t *)env_table->elts;
Packit 90a5c9
    for (j = 0; j < env_table->nelts; ++j) {
Packit 90a5c9
        if (   (!strcmp(env[j].key, GATEWAY_INTERFACE))
Packit 90a5c9
            || (!strcmp(env[j].key, CONTENT_LENGTH))
Packit 90a5c9
            || (!strcmp(env[j].key, SCGI_MAGIC))) {
Packit 90a5c9
            continue;
Packit 90a5c9
        }
Packit 90a5c9
        headerlen += strlen(env[j].key) + strlen(env[j].val) + 2;
Packit 90a5c9
    }
Packit 90a5c9
    bodylen = apr_psprintf(r->pool, "%" APR_OFF_T_FMT, r->remaining);
Packit 90a5c9
    bodylen_size = strlen(bodylen) + 1;
Packit 90a5c9
    headerlen += bodylen_size;
Packit 90a5c9
Packit 90a5c9
    ns_len = apr_psprintf(r->pool, "%" APR_SIZE_T_FMT ":", headerlen);
Packit 90a5c9
    len = strlen(ns_len);
Packit 90a5c9
    headerlen += len + 1; /* 1 == , */
Packit 90a5c9
    cp = buf = apr_palloc(r->pool, headerlen);
Packit 90a5c9
    memcpy(cp, ns_len, len);
Packit 90a5c9
    cp += len;
Packit 90a5c9
Packit 90a5c9
    memcpy(cp, CONTENT_LENGTH, sizeof(CONTENT_LENGTH));
Packit 90a5c9
    cp += sizeof(CONTENT_LENGTH);
Packit 90a5c9
    memcpy(cp, bodylen, bodylen_size);
Packit 90a5c9
    cp += bodylen_size;
Packit 90a5c9
    memcpy(cp, SCGI_MAGIC, sizeof(SCGI_MAGIC));
Packit 90a5c9
    cp += sizeof(SCGI_MAGIC);
Packit 90a5c9
    memcpy(cp, SCGI_PROTOCOL_VERSION, sizeof(SCGI_PROTOCOL_VERSION));
Packit 90a5c9
    cp += sizeof(SCGI_PROTOCOL_VERSION);
Packit 90a5c9
Packit 90a5c9
    for (j = 0; j < env_table->nelts; ++j) {
Packit 90a5c9
        if (   (!strcmp(env[j].key, GATEWAY_INTERFACE))
Packit 90a5c9
            || (!strcmp(env[j].key, CONTENT_LENGTH))
Packit 90a5c9
            || (!strcmp(env[j].key, SCGI_MAGIC))) {
Packit 90a5c9
            continue;
Packit 90a5c9
        }
Packit 90a5c9
        len = strlen(env[j].key) + 1;
Packit 90a5c9
        memcpy(cp, env[j].key, len);
Packit 90a5c9
        cp += len;
Packit 90a5c9
        len = strlen(env[j].val) + 1;
Packit 90a5c9
        memcpy(cp, env[j].val, len);
Packit 90a5c9
        cp += len;
Packit 90a5c9
    }
Packit 90a5c9
    *cp++ = ',';
Packit 90a5c9
Packit 90a5c9
    return sendall(conn, buf, headerlen, r);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Send request body (if any)
Packit 90a5c9
 */
Packit 90a5c9
static int send_request_body(request_rec *r, proxy_conn_rec *conn)
Packit 90a5c9
{
Packit 90a5c9
    if (ap_should_client_block(r)) {
Packit 90a5c9
        char *buf = apr_palloc(r->pool, AP_IOBUFSIZE);
Packit 90a5c9
        int status;
Packit 90a5c9
        long readlen;
Packit 90a5c9
Packit 90a5c9
        readlen = ap_get_client_block(r, buf, AP_IOBUFSIZE);
Packit 90a5c9
        while (readlen > 0) {
Packit 90a5c9
            status = sendall(conn, buf, (apr_size_t)readlen, r);
Packit 90a5c9
            if (status != OK) {
Packit 90a5c9
                return HTTP_SERVICE_UNAVAILABLE;
Packit 90a5c9
            }
Packit 90a5c9
            readlen = ap_get_client_block(r, buf, AP_IOBUFSIZE);
Packit 90a5c9
        }
Packit 90a5c9
        if (readlen == -1) {
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00859)
Packit 90a5c9
                          "receiving request body failed");
Packit 90a5c9
            return HTTP_INTERNAL_SERVER_ERROR;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return OK;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Fetch response from backend and pass back to the front
Packit 90a5c9
 */
Packit 90a5c9
static int pass_response(request_rec *r, proxy_conn_rec *conn)
Packit 90a5c9
{
Packit 90a5c9
    apr_bucket_brigade *bb;
Packit 90a5c9
    apr_bucket *b;
Packit 90a5c9
    const char *location;
Packit 90a5c9
    scgi_config *conf;
Packit 90a5c9
    socket_ex_data *sock_data;
Packit 90a5c9
    int status;
Packit 90a5c9
Packit 90a5c9
    sock_data = apr_palloc(r->pool, sizeof(*sock_data));
Packit 90a5c9
    sock_data->sock = conn->sock;
Packit 90a5c9
    sock_data->counter = &conn->worker->s->read;
Packit 90a5c9
Packit 90a5c9
    bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
Packit 90a5c9
    b = bucket_socket_ex_create(sock_data, r->connection->bucket_alloc);
Packit 90a5c9
    APR_BRIGADE_INSERT_TAIL(bb, b);
Packit 90a5c9
    b = apr_bucket_eos_create(r->connection->bucket_alloc);
Packit 90a5c9
    APR_BRIGADE_INSERT_TAIL(bb, b);
Packit 90a5c9
Packit 90a5c9
    status = ap_scan_script_header_err_brigade_ex(r, bb, NULL,
Packit 90a5c9
                                                  APLOG_MODULE_INDEX);
Packit 90a5c9
    if (status != OK) {
Packit 90a5c9
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00860)
Packit 90a5c9
                      "error reading response headers from %s:%u",
Packit 90a5c9
                      conn->hostname, conn->port);
Packit 90a5c9
        r->status_line = NULL;
Packit 90a5c9
        apr_brigade_destroy(bb);
Packit 90a5c9
        return status;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    conf = ap_get_module_config(r->per_dir_config, &proxy_scgi_module);
Packit 90a5c9
    if (conf->sendfile && conf->sendfile != scgi_sendfile_off) {
Packit 90a5c9
        short err = 1;
Packit 90a5c9
Packit 90a5c9
        location = apr_table_get(r->err_headers_out, conf->sendfile);
Packit 90a5c9
        if (!location) {
Packit 90a5c9
            err = 0;
Packit 90a5c9
            location = apr_table_get(r->headers_out, conf->sendfile);
Packit 90a5c9
        }
Packit 90a5c9
        if (location) {
Packit 90a5c9
            scgi_request_config *req_conf = apr_palloc(r->pool,
Packit 90a5c9
                                                       sizeof(*req_conf));
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00861)
Packit 90a5c9
                          "Found %s: %s - preparing subrequest.",
Packit 90a5c9
                          conf->sendfile, location);
Packit 90a5c9
Packit 90a5c9
            if (err) {
Packit 90a5c9
                apr_table_unset(r->err_headers_out, conf->sendfile);
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                apr_table_unset(r->headers_out, conf->sendfile);
Packit 90a5c9
            }
Packit 90a5c9
            req_conf->location = location;
Packit 90a5c9
            req_conf->type = scgi_sendfile;
Packit 90a5c9
            ap_set_module_config(r->request_config, &proxy_scgi_module,
Packit 90a5c9
                                 req_conf);
Packit 90a5c9
            apr_brigade_destroy(bb);
Packit 90a5c9
            return OK;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (r->status == HTTP_OK 
Packit 90a5c9
        && (!conf->internal_redirect /* default === On */
Packit 90a5c9
            || conf->internal_redirect != scgi_internal_redirect_off)) {
Packit 90a5c9
        short err = 1;
Packit 90a5c9
        const char *location_header = conf->internal_redirect ? 
Packit 90a5c9
            conf->internal_redirect : scgi_internal_redirect_on;
Packit 90a5c9
Packit 90a5c9
        location = apr_table_get(r->err_headers_out, location_header);
Packit 90a5c9
        if (!location) {
Packit 90a5c9
            err = 0;
Packit 90a5c9
            location = apr_table_get(r->headers_out, location_header);
Packit 90a5c9
        }
Packit 90a5c9
        if (location && *location == '/') {
Packit 90a5c9
            scgi_request_config *req_conf = apr_palloc(r->pool,
Packit 90a5c9
                                                       sizeof(*req_conf));
Packit 90a5c9
            if (strcasecmp(location_header, "Location")) {
Packit 90a5c9
                if (err) {
Packit 90a5c9
                    apr_table_unset(r->err_headers_out, location_header);
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    apr_table_unset(r->headers_out, location_header);
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
            req_conf->location = location;
Packit 90a5c9
            req_conf->type = scgi_internal_redirect;
Packit 90a5c9
            ap_set_module_config(r->request_config, &proxy_scgi_module,
Packit 90a5c9
                                 req_conf);
Packit 90a5c9
            apr_brigade_destroy(bb);
Packit 90a5c9
            return OK;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (ap_pass_brigade(r->output_filters, bb)) {
Packit 90a5c9
        return AP_FILTER_ERROR;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return OK;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Internal redirect / subrequest handler, working on request_status hook
Packit 90a5c9
 */
Packit 90a5c9
static int scgi_request_status(int *status, request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    scgi_request_config *req_conf;
Packit 90a5c9
Packit 90a5c9
    if (   (*status == OK)
Packit 90a5c9
        && (req_conf = ap_get_module_config(r->request_config,
Packit 90a5c9
                                            &proxy_scgi_module))) {
Packit 90a5c9
        switch (req_conf->type) {
Packit 90a5c9
        case scgi_internal_redirect:
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00862)
Packit 90a5c9
                          "Internal redirect to %s", req_conf->location);
Packit 90a5c9
Packit 90a5c9
            r->status_line = NULL;
Packit 90a5c9
            if (r->method_number != M_GET) {
Packit 90a5c9
                /* keep HEAD, which is passed around as M_GET, too */
Packit 90a5c9
                r->method = "GET";
Packit 90a5c9
                r->method_number = M_GET;
Packit 90a5c9
            }
Packit 90a5c9
            apr_table_unset(r->headers_in, "Content-Length");
Packit 90a5c9
            ap_internal_redirect_handler(req_conf->location, r);
Packit 90a5c9
            return OK;
Packit 90a5c9
            /* break; */
Packit 90a5c9
Packit 90a5c9
        case scgi_sendfile:
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00863)
Packit 90a5c9
                          "File subrequest to %s", req_conf->location);
Packit 90a5c9
            do {
Packit 90a5c9
                request_rec *rr;
Packit 90a5c9
Packit 90a5c9
                rr = ap_sub_req_lookup_file(req_conf->location, r,
Packit 90a5c9
                                            r->output_filters);
Packit 90a5c9
                if (rr->status == HTTP_OK && rr->finfo.filetype != APR_NOFILE) {
Packit 90a5c9
                    /*
Packit 90a5c9
                     * We don't touch Content-Length here. It might be
Packit 90a5c9
                     * borked (there's plenty of room for a race condition).
Packit 90a5c9
                     * Either the backend sets it or it's gonna be chunked.
Packit 90a5c9
                     */
Packit 90a5c9
                    ap_run_sub_req(rr);
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00864)
Packit 90a5c9
                                  "Subrequest to file '%s' not possible. "
Packit 90a5c9
                                  "(rr->status=%d, rr->finfo.filetype=%d)",
Packit 90a5c9
                                  req_conf->location, rr->status,
Packit 90a5c9
                                  rr->finfo.filetype);
Packit 90a5c9
                    *status = HTTP_INTERNAL_SERVER_ERROR;
Packit 90a5c9
                    return *status;
Packit 90a5c9
                }
Packit 90a5c9
            } while (0);
Packit 90a5c9
Packit 90a5c9
            return OK;
Packit 90a5c9
            /* break; */
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return DECLINED;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * This handles scgi:(dest) URLs
Packit 90a5c9
 */
Packit 90a5c9
static int scgi_handler(request_rec *r, proxy_worker *worker,
Packit 90a5c9
                        proxy_server_conf *conf, char *url,
Packit 90a5c9
                        const char *proxyname, apr_port_t proxyport)
Packit 90a5c9
{
Packit 90a5c9
    int status;
Packit 90a5c9
    proxy_conn_rec *backend = NULL;
Packit 90a5c9
    apr_pool_t *p = r->pool;
Packit 90a5c9
    apr_uri_t *uri;
Packit 90a5c9
    char dummy;
Packit 90a5c9
Packit 90a5c9
    if (strncasecmp(url, SCHEME "://", sizeof(SCHEME) + 2)) {
Packit 90a5c9
        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00865)
Packit 90a5c9
                      "declining URL %s", url);
Packit 90a5c9
        return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* Create space for state information */
Packit 90a5c9
    status = ap_proxy_acquire_connection(PROXY_FUNCTION, &backend, worker,
Packit 90a5c9
                                         r->server);
Packit 90a5c9
    if (status != OK) {
Packit 90a5c9
        goto cleanup;
Packit 90a5c9
    }
Packit 90a5c9
    backend->is_ssl = 0;
Packit 90a5c9
Packit 90a5c9
    /* Step One: Determine Who To Connect To */
Packit 90a5c9
    uri = apr_palloc(p, sizeof(*uri));
Packit 90a5c9
    status = ap_proxy_determine_connection(p, r, conf, worker, backend,
Packit 90a5c9
                                           uri, &url, proxyname, proxyport,
Packit 90a5c9
                                           &dummy, 1);
Packit 90a5c9
    if (status != OK) {
Packit 90a5c9
        goto cleanup;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* Step Two: Make the Connection */
Packit 90a5c9
    if (ap_proxy_connect_backend(PROXY_FUNCTION, backend, worker, r->server)) {
Packit 90a5c9
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00866)
Packit 90a5c9
                      "failed to make connection to backend: %s:%u",
Packit 90a5c9
                      backend->hostname, backend->port);
Packit 90a5c9
        status = HTTP_SERVICE_UNAVAILABLE;
Packit 90a5c9
        goto cleanup;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* Step Three: Process the Request */
Packit 90a5c9
    if (   ((status = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)) != OK)
Packit 90a5c9
        || ((status = send_headers(r, backend)) != OK)
Packit 90a5c9
        || ((status = send_request_body(r, backend)) != OK)
Packit 90a5c9
        || ((status = pass_response(r, backend)) != OK)) {
Packit 90a5c9
        goto cleanup;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
cleanup:
Packit 90a5c9
    if (backend) {
Packit 90a5c9
        backend->close = 1; /* always close the socket */
Packit 90a5c9
        ap_proxy_release_connection(PROXY_FUNCTION, backend, r->server);
Packit 90a5c9
    }
Packit 90a5c9
    return status;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
static void *create_scgi_config(apr_pool_t *p, char *dummy)
Packit 90a5c9
{
Packit 90a5c9
    scgi_config *conf=apr_palloc(p, sizeof(*conf));
Packit 90a5c9
Packit 90a5c9
    conf->sendfile = NULL; /* === default (off) */
Packit 90a5c9
    conf->internal_redirect = NULL; /* === default (on) */
Packit 90a5c9
Packit 90a5c9
    return conf;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
static void *merge_scgi_config(apr_pool_t *p, void *base_, void *add_)
Packit 90a5c9
{
Packit 90a5c9
    scgi_config *base=base_, *add=add_, *conf=apr_palloc(p, sizeof(*conf));
Packit 90a5c9
Packit 90a5c9
    conf->sendfile = add->sendfile ? add->sendfile: base->sendfile;
Packit 90a5c9
    conf->internal_redirect = add->internal_redirect
Packit 90a5c9
                              ? add->internal_redirect
Packit 90a5c9
                              : base->internal_redirect;
Packit 90a5c9
    return conf;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
static const char *scgi_set_send_file(cmd_parms *cmd, void *mconfig,
Packit 90a5c9
                                      const char *arg)
Packit 90a5c9
{
Packit 90a5c9
    scgi_config *conf=mconfig;
Packit 90a5c9
Packit 90a5c9
    if (!strcasecmp(arg, "Off")) {
Packit 90a5c9
        conf->sendfile = scgi_sendfile_off;
Packit 90a5c9
    }
Packit 90a5c9
    else if (!strcasecmp(arg, "On")) {
Packit 90a5c9
        conf->sendfile = scgi_sendfile_on;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        conf->sendfile = arg;
Packit 90a5c9
    }
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
static const char *scgi_set_internal_redirect(cmd_parms *cmd, void *mconfig,
Packit 90a5c9
                                              const char *arg)
Packit 90a5c9
{
Packit 90a5c9
    scgi_config *conf = mconfig;
Packit 90a5c9
Packit 90a5c9
    if (!strcasecmp(arg, "Off")) {
Packit 90a5c9
        conf->internal_redirect = scgi_internal_redirect_off;
Packit 90a5c9
    }
Packit 90a5c9
    else if (!strcasecmp(arg, "On")) {
Packit 90a5c9
        conf->internal_redirect = scgi_internal_redirect_on;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        conf->internal_redirect = arg;
Packit 90a5c9
    }
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
static const command_rec scgi_cmds[] =
Packit 90a5c9
{
Packit 90a5c9
    AP_INIT_TAKE1("ProxySCGISendfile", scgi_set_send_file, NULL,
Packit 90a5c9
                  RSRC_CONF|ACCESS_CONF,
Packit 90a5c9
                  "The name of the X-Sendfile pseudo response header or "
Packit 90a5c9
                  "On or Off"),
Packit 90a5c9
    AP_INIT_TAKE1("ProxySCGIInternalRedirect", scgi_set_internal_redirect, NULL,
Packit 90a5c9
                  RSRC_CONF|ACCESS_CONF,
Packit 90a5c9
                  "The name of the pseudo response header or On or Off"),
Packit 90a5c9
    {NULL}
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
static void register_hooks(apr_pool_t *p)
Packit 90a5c9
{
Packit 90a5c9
    proxy_hook_scheme_handler(scgi_handler, NULL, NULL, APR_HOOK_FIRST);
Packit 90a5c9
    proxy_hook_canon_handler(scgi_canon, NULL, NULL, APR_HOOK_FIRST);
Packit 90a5c9
    APR_OPTIONAL_HOOK(proxy, request_status, scgi_request_status, NULL, NULL,
Packit 90a5c9
                      APR_HOOK_MIDDLE);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
AP_DECLARE_MODULE(proxy_scgi) = {
Packit 90a5c9
    STANDARD20_MODULE_STUFF,
Packit 90a5c9
    create_scgi_config,  /* create per-directory config structure */
Packit 90a5c9
    merge_scgi_config,   /* merge per-directory config structures */
Packit 90a5c9
    NULL,                /* create per-server config structure */
Packit 90a5c9
    NULL,                /* merge per-server config structures */
Packit 90a5c9
    scgi_cmds,           /* command table */
Packit 90a5c9
    register_hooks       /* register hooks */
Packit 90a5c9
};