mod_proxy_scgi.c revision 36ef8f77bffe75d1aa327882be1b5bdbe2ff567a
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Proxy backend module for the SCGI protocol
*
*/
#define APR_WANT_MEMFUNC
#define APR_WANT_STRFUNC
#include "apr_strings.h"
#include "apr_hooks.h"
#include "apr_optional_hooks.h"
#include "apr_buckets.h"
#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
#include "http_protocol.h"
#include "http_request.h"
#include "util_script.h"
#include "mod_proxy.h"
#define SCHEME "scgi"
#define PROXY_FUNCTION "SCGI"
#define SCGI_MAGIC "SCGI"
#define SCGI_PROTOCOL_VERSION "1"
#define SCGI_DEFAULT_PORT (4000)
/* just protect from typos */
#define CONTENT_LENGTH "CONTENT_LENGTH"
#define GATEWAY_INTERFACE "GATEWAY_INTERFACE"
typedef enum {
typedef struct {
const char *location; /* target URL */
const char *scgi_sendfile_off = "off";
const char *scgi_sendfile_on = "X-Sendfile";
typedef struct {
const char *sendfile;
int internal_redirect;
} scgi_config;
/*
* We create our own bucket type, which is actually derived (c&p) from the
* socket bucket.
* Maybe some time this should be made more abstract (like passing an
* interception function to read or something) and go into the ap_ or
* even apr_ namespace.
*/
typedef struct {
{
char *buf;
if (block == APR_NONBLOCK_READ) {
apr_socket_timeout_set(p, 0);
}
if (block == APR_NONBLOCK_READ) {
}
return rv;
}
if (*len > 0) {
apr_bucket_heap *h;
/* count for stats */
/* Change the current bucket to refer to what we read */
h = a->data;
}
else {
a = apr_bucket_immortal_make(a, "", 0);
}
return APR_SUCCESS;
}
static const apr_bucket_type_t bucket_type_socket_ex = {
};
{
b->type = &bucket_type_socket_ex;
b->start = -1;
return b;
}
{
APR_BUCKET_INIT(b);
b->free = apr_bucket_free;
return bucket_socket_ex_make(b, data);
}
/*
* Canonicalize scgi-like URLs.
*/
{
return DECLINED;
}
if (err) {
return HTTP_BAD_REQUEST;
}
}
r->proxyreq);
if (!path) {
return HTTP_BAD_REQUEST;
}
return OK;
}
/*
* Send a block of data, ensure, everything is sent
*/
request_rec *r)
{
while (length > 0) {
return HTTP_SERVICE_UNAVAILABLE;
}
/* count for stats */
}
return OK;
}
/*
* Send SCGI header block
*/
{
const char *ns_len;
const apr_array_header_t *env_table;
const apr_table_entry_t *env;
int j;
+ sizeof(SCGI_MAGIC)
+ sizeof(SCGI_PROTOCOL_VERSION);
ap_add_cgi_vars(r);
/*
* The header blob basically takes the environment and concatenates
* keys and values using 0 bytes. There are special treatments here:
* - GATEWAY_INTERFACE and SCGI_MAGIC are dropped
* - CONTENT_LENGTH is always set and must be sent as the very first
* variable
*
* Additionally it's wrapped into a so-called netstring (see SCGI spec)
*/
continue;
}
}
cp += sizeof(CONTENT_LENGTH);
cp += bodylen_size;
cp += sizeof(SCGI_MAGIC);
cp += sizeof(SCGI_PROTOCOL_VERSION);
continue;
}
}
*cp++ = ',';
}
/*
* Send request body (if any)
*/
{
if (ap_should_client_block(r)) {
int status;
while (readlen > 0) {
return HTTP_SERVICE_UNAVAILABLE;
}
}
if (readlen == -1) {
"failed");
return HTTP_INTERNAL_SERVER_ERROR;
}
}
return OK;
}
/*
* Fetch response from backend and pass back to the front
*/
{
apr_bucket *b;
const char *location;
int status;
r->status_line = NULL;
return status;
}
short err = 1;
if (!location) {
err = 0;
}
if (location) {
sizeof(*req_conf));
"preparing subrequest.",
if (err) {
}
else {
}
req_conf);
return OK;
}
}
sizeof(*req_conf));
req_conf);
return OK;
}
}
/* XXX: What could we do with that return code? */
return OK;
}
/*
* Internal redirect / subrequest handler, working on request_status hook
*/
{
&proxy_scgi_module))) {
case scgi_internal_redirect:
r->status_line = NULL;
if (r->method_number != M_GET) {
/* keep HEAD, which is passed around as M_GET, too */
r->method = "GET";
r->method_number = M_GET;
}
return OK;
/* break; */
case scgi_sendfile:
do {
r->output_filters);
/*
* We don't touch Content-Length here. It might be
* borked (there's plenty of room for a race condition).
* Either the backend sets it or it's gonna be chunked.
*/
}
else {
"Subrequest to file '%s' not possible. "
"(rr->status=%d, rr->finfo.filetype=%d)",
return *status;
}
} while(0);
return OK;
/* break; */
}
}
return DECLINED;
}
/*
* This handles scgi:(dest) URLs
*/
{
int status;
apr_pool_t *p = r->pool;
char dummy;
return DECLINED;
}
/* Create space for state information */
r->server);
goto cleanup;
}
/* Step One: Determine Who To Connect To */
&dummy, 1);
goto cleanup;
}
/* Step Two: Make the Connection */
goto cleanup;
}
/* Step Three: Process the Request */
goto cleanup;
}
if (backend) {
}
return status;
}
{
return conf;
}
{
return conf;
}
const char *arg)
{
}
}
else {
}
return NULL;
}
static const command_rec scgi_cmds[] =
{
"The name of the X-Sendfile peudo response header or "
"On or Off"),
"Off if internal redirect responses should not be accepted"),
{NULL}
};
static void register_hooks(apr_pool_t *p)
{
}
create_scgi_config, /* create per-directory config structure */
merge_scgi_config, /* merge per-directory config structures */
NULL, /* create per-server config structure */
NULL, /* merge per-server config structures */
scgi_cmds, /* command table */
register_hooks /* register hooks */
};