mirror of
https://github.com/clearlinux/uwsgi.git
synced 2026-08-29 02:05:38 +00:00
398 lines
11 KiB
C
398 lines
11 KiB
C
#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 UWSGI_SCHEME "uwsgi"
|
|
#define UWSGI_DEFAULT_PORT 3031
|
|
|
|
module AP_MODULE_DECLARE_DATA proxy_uwsgi_module;
|
|
|
|
|
|
static int uwsgi_canon(request_rec *r, char *url)
|
|
{
|
|
char *host, sport[sizeof(":65535")];
|
|
const char *err, *path;
|
|
apr_port_t port = UWSGI_DEFAULT_PORT;
|
|
|
|
if (strncasecmp(url, UWSGI_SCHEME "://", sizeof(UWSGI_SCHEME) + 2)) {
|
|
return DECLINED;
|
|
}
|
|
url += sizeof(UWSGI_SCHEME); /* Keep slashes */
|
|
|
|
err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
|
|
if (err) {
|
|
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
|
"error parsing URL %s: %s", url, err);
|
|
return HTTP_BAD_REQUEST;
|
|
}
|
|
|
|
apr_snprintf(sport, sizeof(sport), ":%u", port);
|
|
|
|
if (ap_strchr(host, ':')) { /* if literal IPv6 address */
|
|
host = apr_pstrcat(r->pool, "[", host, "]", NULL);
|
|
}
|
|
|
|
path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
|
|
r->proxyreq);
|
|
if (!path) {
|
|
return HTTP_BAD_REQUEST;
|
|
}
|
|
|
|
r->filename = apr_pstrcat(r->pool, "proxy:" UWSGI_SCHEME "://", host, sport, "/",
|
|
path, NULL);
|
|
|
|
return OK;
|
|
}
|
|
|
|
|
|
static int uwsgi_send(proxy_conn_rec *conn, const char *buf, apr_size_t length,
|
|
request_rec *r)
|
|
{
|
|
apr_status_t rv;
|
|
apr_size_t written;
|
|
|
|
while (length > 0) {
|
|
written = length;
|
|
if ((rv = apr_socket_send(conn->sock, buf, &written)) != APR_SUCCESS) {
|
|
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
|
|
"sending data to %s:%u failed",
|
|
conn->hostname, conn->port);
|
|
return HTTP_SERVICE_UNAVAILABLE;
|
|
}
|
|
|
|
/* count for stats */
|
|
conn->worker->s->transferred += written;
|
|
buf += written;
|
|
length -= written;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
|
|
/*
|
|
* Send uwsgi header block
|
|
*/
|
|
static int uwsgi_send_headers(request_rec *r, proxy_conn_rec *conn)
|
|
{
|
|
char *buf, *ptr;
|
|
|
|
const apr_array_header_t *env_table;
|
|
const apr_table_entry_t *env;
|
|
|
|
int j;
|
|
|
|
apr_size_t headerlen = 4;
|
|
uint16_t pktsize, keylen, vallen;
|
|
|
|
ap_add_common_vars(r);
|
|
ap_add_cgi_vars(r);
|
|
|
|
const char *script_name = apr_table_get(r->subprocess_env, "SCRIPT_NAME");
|
|
const char *path_info = apr_table_get(r->subprocess_env, "PATH_INFO");
|
|
if (script_name && path_info) {
|
|
apr_table_set(r->subprocess_env, "SCRIPT_NAME", apr_pstrndup(r->pool, script_name, strlen(script_name)-strlen(path_info)));
|
|
}
|
|
|
|
env_table = apr_table_elts(r->subprocess_env);
|
|
env = (apr_table_entry_t *)env_table->elts;
|
|
|
|
for (j = 0; j < env_table->nelts; ++j) {
|
|
headerlen += 2 + strlen(env[j].key) + 2 + strlen(env[j].val) ;
|
|
}
|
|
|
|
ptr = buf = apr_palloc(r->pool, headerlen);
|
|
|
|
ptr+=4;
|
|
|
|
for (j = 0; j < env_table->nelts; ++j) {
|
|
keylen = strlen(env[j].key);
|
|
*ptr++= (uint8_t) (keylen & 0xff);
|
|
*ptr++= (uint8_t) ((keylen >> 8) & 0xff);
|
|
memcpy(ptr, env[j].key, keylen) ; ptr+=keylen;
|
|
|
|
vallen = strlen(env[j].val);
|
|
*ptr++= (uint8_t) (vallen & 0xff);
|
|
*ptr++= (uint8_t) ((vallen >> 8) & 0xff);
|
|
memcpy(ptr, env[j].val, vallen) ; ptr+=vallen;
|
|
}
|
|
|
|
pktsize = headerlen-4;
|
|
|
|
buf[0] = 0;
|
|
buf[1] = (uint8_t) (pktsize & 0xff);
|
|
buf[2] = (uint8_t) ((pktsize >> 8) & 0xff);
|
|
buf[0] = 0;
|
|
|
|
return uwsgi_send(conn, buf, headerlen, r);
|
|
}
|
|
|
|
|
|
static int uwsgi_send_body(request_rec *r, proxy_conn_rec *conn)
|
|
{
|
|
if (ap_should_client_block(r)) {
|
|
char *buf = apr_palloc(r->pool, AP_IOBUFSIZE);
|
|
int status;
|
|
apr_size_t readlen;
|
|
|
|
readlen = ap_get_client_block(r, buf, AP_IOBUFSIZE);
|
|
while (readlen > 0) {
|
|
status = uwsgi_send(conn, buf, readlen, r);
|
|
if (status != OK) {
|
|
return HTTP_SERVICE_UNAVAILABLE;
|
|
}
|
|
readlen = ap_get_client_block(r, buf, AP_IOBUFSIZE);
|
|
}
|
|
if (readlen == -1) {
|
|
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
|
"receiving request body failed");
|
|
return HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
static
|
|
apr_status_t ap_proxygetline(apr_bucket_brigade *bb, char *s, int n, request_rec *r,
|
|
int fold, int *writen)
|
|
{
|
|
char *tmp_s = s;
|
|
apr_status_t rv;
|
|
apr_size_t len;
|
|
|
|
rv = ap_rgetline(&tmp_s, n, &len, r, fold, bb);
|
|
apr_brigade_cleanup(bb);
|
|
|
|
if (rv == APR_SUCCESS) {
|
|
*writen = (int) len;
|
|
} else if (rv == APR_ENOSPC) {
|
|
*writen = n;
|
|
} else {
|
|
*writen = -1;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
static int uwsgi_response(request_rec *r, proxy_conn_rec *backend, proxy_server_conf *conf)
|
|
{
|
|
|
|
char buffer[HUGE_STRING_LEN];
|
|
const char *buf;
|
|
char *value, *end;
|
|
int len;
|
|
apr_status_t rc;
|
|
conn_rec *c = r->connection;
|
|
apr_off_t readbytes;
|
|
apr_status_t rv;
|
|
apr_bucket *e;
|
|
apr_read_type_e mode = APR_NONBLOCK_READ;
|
|
|
|
request_rec *rp = ap_proxy_make_fake_req(backend->connection, r);
|
|
rp->proxyreq = PROXYREQ_RESPONSE;
|
|
|
|
apr_bucket_brigade *bb = apr_brigade_create(r->pool, c->bucket_alloc);
|
|
|
|
|
|
rc = ap_proxygetline(bb, buffer, sizeof(buffer), rp, 0, &len);
|
|
if (len == 0) {
|
|
rc = ap_proxygetline(bb, buffer, sizeof(buffer), rp, 0, &len);
|
|
}
|
|
|
|
if (len <= 0) {
|
|
// oops
|
|
return HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
backend->worker->s->read += len;
|
|
|
|
if (!apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
|
|
// oops
|
|
return HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
int major, minor;
|
|
|
|
major = buffer[5] - '0';
|
|
minor = buffer[7] - '0';
|
|
|
|
/* If not an HTTP/1 message or
|
|
* if the status line was > 8192 bytes
|
|
*/
|
|
if ((major != 1) || (len >= sizeof(buffer)-1)) {
|
|
return ap_proxyerror(r, HTTP_BAD_GATEWAY,
|
|
apr_pstrcat(r->pool, "Corrupt status line returned by remote "
|
|
"server: ", buffer, NULL));
|
|
}
|
|
|
|
char keepchar = buffer[12];
|
|
buffer[12] = '\0';
|
|
r->status = atoi(&buffer[9]);
|
|
|
|
if (keepchar != '\0') {
|
|
buffer[12] = keepchar;
|
|
} else {
|
|
/* 2616 requires the space in Status-Line; the origin
|
|
* server may have sent one but ap_rgetline_core will
|
|
* have stripped it. */
|
|
buffer[12] = ' ';
|
|
buffer[13] = '\0';
|
|
}
|
|
r->status_line = apr_pstrdup(r->pool, &buffer[9]);
|
|
|
|
// start parsing headers;
|
|
while ((len = ap_getline(buffer, sizeof(buffer), rp, 1)) > 0) {
|
|
value = strchr(buffer, ':');
|
|
// invalid header skip
|
|
if (!value) continue;
|
|
*value = '\0';
|
|
++value;
|
|
while (apr_isspace(*value)) ++value;
|
|
for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --end) *end = '\0';
|
|
apr_table_add(r->headers_out, buffer, value);
|
|
}
|
|
|
|
|
|
if ((buf = apr_table_get(r->headers_out, "Content-Type"))) {
|
|
ap_set_content_type(r, apr_pstrdup(r->pool, buf));
|
|
}
|
|
|
|
for(;;) {
|
|
rv = ap_get_brigade(rp->input_filters, bb,
|
|
AP_MODE_READBYTES, mode,
|
|
conf->io_buffer_size);
|
|
if (APR_STATUS_IS_EAGAIN(rv)
|
|
|| (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb))) {
|
|
e = apr_bucket_flush_create(c->bucket_alloc);
|
|
APR_BRIGADE_INSERT_TAIL(bb, e);
|
|
if (ap_pass_brigade(r->output_filters, bb) || c->aborted) {
|
|
break;
|
|
}
|
|
apr_brigade_cleanup(bb);
|
|
mode = APR_BLOCK_READ;
|
|
continue;
|
|
}
|
|
else if (rv == APR_EOF) {
|
|
break;
|
|
}
|
|
else if (rv != APR_SUCCESS) {
|
|
ap_proxy_backend_broke(r, bb);
|
|
ap_pass_brigade(r->output_filters, bb);
|
|
}
|
|
|
|
mode = APR_NONBLOCK_READ;
|
|
apr_brigade_length(bb, 0, &readbytes);
|
|
backend->worker->s->read += readbytes;
|
|
ap_pass_brigade(r->output_filters, bb);
|
|
apr_brigade_cleanup(bb);
|
|
}
|
|
e = apr_bucket_eos_create(c->bucket_alloc);
|
|
APR_BRIGADE_INSERT_TAIL(bb, e);
|
|
ap_pass_brigade(r->output_filters, bb);
|
|
|
|
apr_brigade_cleanup(bb);
|
|
|
|
return OK;
|
|
}
|
|
|
|
static int uwsgi_handler(request_rec *r, proxy_worker *worker,
|
|
proxy_server_conf *conf, char *url,
|
|
const char *proxyname, apr_port_t proxyport)
|
|
{
|
|
int status;
|
|
proxy_conn_rec *backend = NULL;
|
|
apr_pool_t *p = r->pool;
|
|
apr_uri_t *uri = apr_palloc(r->pool, sizeof(*uri));
|
|
char server_portstr[32];
|
|
|
|
if (strncasecmp(url, UWSGI_SCHEME "://", sizeof(UWSGI_SCHEME) + 2)) {
|
|
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
|
|
"declining URL %s", url);
|
|
return DECLINED;
|
|
}
|
|
|
|
// ADD PATH_INFO
|
|
apr_table_add(r->subprocess_env, "PATH_INFO", url+strlen(worker->name));
|
|
|
|
|
|
/* Create space for state information */
|
|
status = ap_proxy_acquire_connection(UWSGI_SCHEME, &backend, worker,
|
|
r->server);
|
|
if (status != OK) {
|
|
goto cleanup;
|
|
}
|
|
backend->is_ssl = 0;
|
|
|
|
/* Step One: Determine Who To Connect To */
|
|
status = ap_proxy_determine_connection(p, r, conf, worker, backend,
|
|
uri, &url, proxyname, proxyport,
|
|
server_portstr, sizeof(server_portstr));
|
|
if (status != OK) {
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
/* Step Two: Make the Connection */
|
|
if (ap_proxy_connect_backend(UWSGI_SCHEME, backend, worker, r->server)) {
|
|
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
|
"failed to make connection to backend: %s:%u",
|
|
backend->hostname, backend->port);
|
|
status = HTTP_SERVICE_UNAVAILABLE;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Step Three: Create conn_rec */
|
|
if (!backend->connection) {
|
|
if ((status = ap_proxy_connection_create(UWSGI_SCHEME, backend,
|
|
r->connection, r->server)) != OK)
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Step Four: Process the Request */
|
|
if ( ((status = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)) != OK)
|
|
|| ((status = uwsgi_send_headers(r, backend)) != OK)
|
|
|| ((status = uwsgi_send_body(r, backend)) != OK)
|
|
|| ((status = uwsgi_response(r, backend, conf)) != OK)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
if (backend) {
|
|
backend->close = 1; /* always close the socket */
|
|
ap_proxy_release_connection(UWSGI_SCHEME, backend, r->server);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
static void register_hooks(apr_pool_t *p)
|
|
{
|
|
proxy_hook_scheme_handler(uwsgi_handler, NULL, NULL, APR_HOOK_FIRST);
|
|
proxy_hook_canon_handler(uwsgi_canon, NULL, NULL, APR_HOOK_FIRST);
|
|
}
|
|
|
|
|
|
module AP_MODULE_DECLARE_DATA proxy_uwsgi_module = {
|
|
STANDARD20_MODULE_STUFF,
|
|
NULL, /* create per-directory config structure */
|
|
NULL, /* merge per-directory config structures */
|
|
NULL, /* create per-server config structure */
|
|
NULL, /* merge per-server config structures */
|
|
NULL, /* command table */
|
|
register_hooks /* register hooks */
|
|
};
|