|
|
|
#include "first.h"
|
|
|
|
|
|
|
|
#include "buffer.h"
|
|
|
|
#include "server.h"
|
|
|
|
#include "keyvalue.h"
|
|
|
|
#include "log.h"
|
|
|
|
|
|
|
|
#include "http_chunk.h"
|
|
|
|
#include "fdevent.h"
|
|
|
|
#include "connections.h"
|
|
|
|
#include "response.h"
|
|
|
|
#include "joblist.h"
|
|
|
|
|
|
|
|
#include "plugin.h"
|
|
|
|
|
|
|
|
#include "inet_ntop_cache.h"
|
|
|
|
#include "crc32.h"
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include "sys-socket.h"
|
|
|
|
|
|
|
|
#define data_proxy data_fastcgi
|
|
|
|
#define data_proxy_init data_fastcgi_init
|
|
|
|
|
|
|
|
#define PROXY_RETRY_TIMEOUT 60
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* the proxy module is based on the fastcgi module
|
|
|
|
*
|
|
|
|
* 28.06.2004 Jan Kneschke The first release
|
|
|
|
* 01.07.2004 Evgeny Rodichev Several bugfixes and cleanups
|
|
|
|
* - co-ordinate up- and downstream flows correctly (proxy_demux_response
|
|
|
|
* and proxy_handle_fdevent)
|
|
|
|
* - correctly transfer upstream http_response_status;
|
|
|
|
* - some unused structures removed.
|
|
|
|
*
|
|
|
|
* TODO: - delay upstream read if write_queue is too large
|
|
|
|
* (to prevent memory eating, like in apache). Shoud be
|
|
|
|
* configurable).
|
|
|
|
* - persistent connection with upstream servers
|
|
|
|
* - HTTP/1.1
|
|
|
|
*/
|
|
|
|
typedef enum {
|
|
|
|
PROXY_BALANCE_UNSET,
|
|
|
|
PROXY_BALANCE_FAIR,
|
|
|
|
PROXY_BALANCE_HASH,
|
|
|
|
PROXY_BALANCE_RR
|
|
|
|
} proxy_balance_t;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
array *extensions;
|
|
|
|
unsigned short debug;
|
|
|
|
|
|
|
|
proxy_balance_t balance;
|
|
|
|
} plugin_config;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
PLUGIN_DATA;
|
|
|
|
|
|
|
|
buffer *parse_response;
|
|
|
|
buffer *balance_buf;
|
|
|
|
|
|
|
|
plugin_config **config_storage;
|
|
|
|
|
|
|
|
plugin_config conf;
|
|
|
|
} plugin_data;
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
PROXY_STATE_INIT,
|
|
|
|
PROXY_STATE_CONNECT,
|
|
|
|
PROXY_STATE_PREPARE_WRITE,
|
|
|
|
PROXY_STATE_WRITE,
|
|
|
|
PROXY_STATE_READ
|
|
|
|
} proxy_connection_state_t;
|
|
|
|
|
|
|
|
enum { PROXY_STDOUT, PROXY_END_REQUEST };
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
proxy_connection_state_t state;
|
|
|
|
time_t state_timestamp;
|
|
|
|
|
|
|
|
data_proxy *host;
|
|
|
|
|
|
|
|
buffer *response;
|
|
|
|
buffer *response_header;
|
|
|
|
|
|
|
|
chunkqueue *wb;
|
|
|
|
off_t wb_reqlen;
|
|
|
|
|
|
|
|
int fd; /* fd to the proxy process */
|
|
|
|
int fde_ndx; /* index into the fd-event buffer */
|
|
|
|
|
|
|
|
size_t path_info_offset; /* start of path_info in uri.path */
|
|
|
|
|
|
|
|
connection *remote_conn; /* dump pointer */
|
|
|
|
plugin_data *plugin_data; /* dump pointer */
|
|
|
|
} handler_ctx;
|
|
|
|
|
|
|
|
|
|
|
|
/* ok, we need a prototype */
|
|
|
|
static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents);
|
|
|
|
|
|
|
|
static handler_ctx * handler_ctx_init(void) {
|
|
|
|
handler_ctx * hctx;
|
|
|
|
|
|
|
|
|
|
|
|
hctx = calloc(1, sizeof(*hctx));
|
|
|
|
|
|
|
|
hctx->state = PROXY_STATE_INIT;
|
|
|
|
hctx->host = NULL;
|
|
|
|
|
|
|
|
hctx->response = buffer_init();
|
|
|
|
hctx->response_header = buffer_init();
|
|
|
|
|
|
|
|
hctx->wb = chunkqueue_init();
|
|
|
|
hctx->wb_reqlen = 0;
|
|
|
|
|
|
|
|
hctx->fd = -1;
|
|
|
|
hctx->fde_ndx = -1;
|
|
|
|
|
|
|
|
return hctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handler_ctx_free(handler_ctx *hctx) {
|
|
|
|
buffer_free(hctx->response);
|
|
|
|
buffer_free(hctx->response_header);
|
|
|
|
chunkqueue_free(hctx->wb);
|
|
|
|
|
|
|
|
free(hctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
INIT_FUNC(mod_proxy_init) {
|
|
|
|
plugin_data *p;
|
|
|
|
|
|
|
|
p = calloc(1, sizeof(*p));
|
|
|
|
|
|
|
|
p->parse_response = buffer_init();
|
|
|
|
p->balance_buf = buffer_init();
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FREE_FUNC(mod_proxy_free) {
|
|
|
|
plugin_data *p = p_d;
|
|
|
|
|
|
|
|
UNUSED(srv);
|
|
|
|
|
|
|
|
buffer_free(p->parse_response);
|
|
|
|
buffer_free(p->balance_buf);
|
|
|
|
|
|
|
|
if (p->config_storage) {
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < srv->config_context->used; i++) {
|
|
|
|
plugin_config *s = p->config_storage[i];
|
|
|
|
|
|
|
|
if (NULL == s) continue;
|
|
|
|
|
|
|
|
array_free(s->extensions);
|
|
|
|
|
|
|
|
free(s);
|
|
|
|
}
|
|
|
|
free(p->config_storage);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(p);
|
|
|
|
|
|
|
|
return HANDLER_GO_ON;
|
|
|
|
}
|
|
|
|
|
|
|
|
SETDEFAULTS_FUNC(mod_proxy_set_defaults) {
|
|
|
|
plugin_data *p = p_d;
|
|
|
|
data_unset *du;
|
|
|
|
size_t i = 0;
|
|
|
|
|
|
|
|
config_values_t cv[] = {
|
|
|
|
{ "proxy.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
|
|
|
|
{ "proxy.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
|
|
|
|
{ "proxy.balance", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
|
|
|
|
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
|
|
|
|
};
|
|
|
|
|
|
|
|
p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
|
|
|
|
|
|
|
|
for (i = 0; i < srv->config_context->used; i++) {
|
|
|
|
data_config const* config = (data_config const*)srv->config_context->data[i];
|
|
|
|
plugin_config *s;
|
|
|
|
|
|
|
|
s = malloc(sizeof(plugin_config));
|
|
|
|
s->extensions = array_init();
|
|
|
|
s->debug = 0;
|
|
|
|
|
|
|
|
cv[0].destination = s->extensions;
|
|
|
|
cv[1].destination = &(s->debug);
|
|
|
|
cv[2].destination = p->balance_buf;
|
|
|
|
|
|
|
|
buffer_reset(p->balance_buf);
|
|
|
|
|
|
|
|
p->config_storage[i] = s;
|
|
|
|
|
|
|
|
if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buffer_string_is_empty(p->balance_buf)) {
|
|
|
|
s->balance = PROXY_BALANCE_FAIR;
|
|
|
|
} else if (buffer_is_equal_string(p->balance_buf, CONST_STR_LEN("fair"))) {
|
|
|
|
s->balance = PROXY_BALANCE_FAIR;
|
|
|
|
} else if (buffer_is_equal_string(p->balance_buf, CONST_STR_LEN("round-robin"))) {
|
|
|
|
s->balance = PROXY_BALANCE_RR;
|
|
|
|
} else if (buffer_is_equal_string(p->balance_buf, CONST_STR_LEN("hash"))) {
|
|
|
|
s->balance = PROXY_BALANCE_HASH;
|
|
|
|
} else {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sb",
|
|
|
|
"proxy.balance has to be one of: fair, round-robin, hash, but not:", p->balance_buf);
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL != (du = array_get_element(config->value, "proxy.server"))) {
|
|
|
|
size_t j;
|
|
|
|
data_array *da = (data_array *)du;
|
|
|
|
|
|
|
|
if (du->type != TYPE_ARRAY) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sss",
|
|
|
|
"unexpected type for key: ", "proxy.server", "expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
|
|
|
|
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* proxy.server = ( "<ext>" => ...,
|
|
|
|
* "<ext>" => ... )
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (j = 0; j < da->value->used; j++) {
|
|
|
|
data_array *da_ext = (data_array *)da->value->data[j];
|
|
|
|
size_t n;
|
|
|
|
|
|
|
|
if (da_ext->type != TYPE_ARRAY) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sssbs",
|
|
|
|
"unexpected type for key: ", "proxy.server",
|
|
|
|
"[", da->value->data[j]->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
|
|
|
|
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* proxy.server = ( "<ext>" =>
|
|
|
|
* ( "<host>" => ( ... ),
|
|
|
|
* "<host>" => ( ... )
|
|
|
|
* ),
|
|
|
|
* "<ext>" => ... )
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (n = 0; n < da_ext->value->used; n++) {
|
|
|
|
data_array *da_host = (data_array *)da_ext->value->data[n];
|
|
|
|
|
|
|
|
data_proxy *df;
|
|
|
|
data_array *dfa;
|
|
|
|
|
|
|
|
config_values_t pcv[] = {
|
|
|
|
{ "host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
|
|
|
|
{ "port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
|
|
|
|
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
|
|
|
|
};
|
|
|
|
|
|
|
|
if (da_host->type != TYPE_ARRAY) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "ssSBS",
|
|
|
|
"unexpected type for key:",
|
|
|
|
"proxy.server",
|
|
|
|
"[", da_ext->value->data[n]->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
|
|
|
|
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
df = data_proxy_init();
|
|
|
|
|
|
|
|
df->port = 80;
|
|
|
|
|
|
|
|
buffer_copy_buffer(df->key, da_host->key);
|
|
|
|
|
|
|
|
pcv[0].destination = df->host;
|
|
|
|
pcv[1].destination = &(df->port);
|
|
|
|
|
|
|
|
if (0 != config_insert_values_internal(srv, da_host->value, pcv, T_CONFIG_SCOPE_CONNECTION)) {
|
|
|
|
df->free((data_unset*) df);
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buffer_string_is_empty(df->host)) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sbbbs",
|
|
|
|
"missing key (string):",
|
|
|
|
da->key,
|
|
|
|
da_ext->key,
|
|
|
|
da_host->key,
|
|
|
|
"host");
|
|
|
|
|
|
|
|
df->free((data_unset*) df);
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if extension already exists, take it */
|
|
|
|
|
|
|
|
if (NULL == (dfa = (data_array *)array_get_element(s->extensions, da_ext->key->ptr))) {
|
|
|
|
dfa = data_array_init();
|
|
|
|
|
|
|
|
buffer_copy_buffer(dfa->key, da_ext->key);
|
|
|
|
|
|
|
|
array_insert_unique(dfa->value, (data_unset *)df);
|
|
|
|
array_insert_unique(s->extensions, (data_unset *)dfa);
|
|
|
|
} else {
|
|
|
|
array_insert_unique(dfa->value, (data_unset *)df);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return HANDLER_GO_ON;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void proxy_connection_close(server *srv, handler_ctx *hctx) {
|
|
|
|
plugin_data *p;
|
|
|
|
connection *con;
|
|
|
|
|
|
|
|
p = hctx->plugin_data;
|
|
|
|
con = hctx->remote_conn;
|
|
|
|
|
|
|
|
if (hctx->fd != -1) {
|
|
|
|
fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
|
|
|
|
fdevent_unregister(srv->ev, hctx->fd);
|
|
|
|
|
|
|
|
close(hctx->fd);
|
|
|
|
srv->cur_fds--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hctx->host) {
|
|
|
|
hctx->host->usage--;
|
|
|
|
}
|
|
|
|
|
|
|
|
handler_ctx_free(hctx);
|
|
|
|
con->plugin_ctx[p->id] = NULL;
|
|
|
|
|
|
|
|
/* finish response (if not already con->file_started, con->file_finished) */
|
|
|
|
if (con->mode == p->id) {
|
|
|
|
http_response_backend_done(srv, con);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int proxy_establish_connection(server *srv, handler_ctx *hctx) {
|
|
|
|
struct sockaddr *proxy_addr;
|
|
|
|
struct sockaddr_in proxy_addr_in;
|
|
|
|
#if defined(HAVE_SYS_UN_H)
|
|
|
|
struct sockaddr_un proxy_addr_un;
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
|
|
|
|
struct sockaddr_in6 proxy_addr_in6;
|
|
|
|
#endif
|
|
|
|
socklen_t servlen;
|
|
|
|
|
|
|
|
plugin_data *p = hctx->plugin_data;
|
|
|
|
data_proxy *host= hctx->host;
|
|
|
|
int proxy_fd = hctx->fd;
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(HAVE_SYS_UN_H)
|
|
|
|
if (strstr(host->host->ptr, "/")) {
|
|
|
|
if (buffer_string_length(host->host) + 1 > sizeof(proxy_addr_un.sun_path)) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sB",
|
|
|
|
"ERROR: Unix Domain socket filename too long:",
|
|
|
|
host->host);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&proxy_addr_un, 0, sizeof(proxy_addr_un));
|
|
|
|
proxy_addr_un.sun_family = AF_UNIX;
|
|
|
|
strcpy(proxy_addr_un.sun_path, host->host->ptr);
|
|
|
|
servlen = sizeof(proxy_addr_un);
|
|
|
|
proxy_addr = (struct sockaddr *) &proxy_addr_un;
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
|
|
|
|
if (strstr(host->host->ptr, ":")) {
|
|
|
|
memset(&proxy_addr_in6, 0, sizeof(proxy_addr_in6));
|
|
|
|
proxy_addr_in6.sin6_family = AF_INET6;
|
|
|
|
inet_pton(AF_INET6, host->host->ptr, (char *) &proxy_addr_in6.sin6_addr);
|
|
|
|
proxy_addr_in6.sin6_port = htons(host->port);
|
|
|
|
servlen = sizeof(proxy_addr_in6);
|
|
|
|
proxy_addr = (struct sockaddr *) &proxy_addr_in6;
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
memset(&proxy_addr_in, 0, sizeof(proxy_addr_in));
|
|
|
|
proxy_addr_in.sin_family = AF_INET;
|
|
|
|
proxy_addr_in.sin_addr.s_addr = inet_addr(host->host->ptr);
|
|
|
|
proxy_addr_in.sin_port = htons(host->port);
|
|
|
|
servlen = sizeof(proxy_addr_in);
|
|
|
|
proxy_addr = (struct sockaddr *) &proxy_addr_in;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (-1 == connect(proxy_fd, proxy_addr, servlen)) {
|
|
|
|
if (errno == EINPROGRESS || errno == EALREADY) {
|
|
|
|
if (p->conf.debug) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sd",
|
|
|
|
"connect delayed:", proxy_fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sdsd",
|
|
|
|
"connect failed:", proxy_fd, strerror(errno), errno);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (p->conf.debug) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sd",
|
|
|
|
"connect succeeded: ", proxy_fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void proxy_set_header(connection *con, const char *key, const char *value) {
|
|
|
|
data_string *ds_dst;
|
|
|
|
|
|
|
|
if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
|
|
|
|
ds_dst = data_string_init();
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer_copy_string(ds_dst->key, key);
|
|
|
|
buffer_copy_string(ds_dst->value, value);
|
|
|
|
array_insert_unique(con->request.headers, (data_unset *)ds_dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void proxy_append_header(connection *con, const char *key, const char *value) {
|
|
|
|
data_string *ds_dst;
|
|
|
|
|
|
|
|
if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
|
|
|
|
ds_dst = data_string_init();
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer_copy_string(ds_dst->key, key);
|
|
|
|
buffer_append_string(ds_dst->value, value);
|
|
|
|
array_insert_unique(con->request.headers, (data_unset *)ds_dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int proxy_create_env(server *srv, handler_ctx *hctx) {
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
connection *con = hctx->remote_conn;
|
|
|
|
buffer *b;
|
|
|
|
|
|
|
|
/* build header */
|
|
|
|
|
|
|
|
b = buffer_init();
|
|
|
|
|
|
|
|
/* request line */
|
|
|
|
buffer_copy_string(b, get_http_method_name(con->request.http_method));
|
|
|
|
buffer_append_string_len(b, CONST_STR_LEN(" "));
|
|
|
|
|
|
|
|
buffer_append_string_buffer(b, con->request.uri);
|
|
|
|
buffer_append_string_len(b, CONST_STR_LEN(" HTTP/1.0\r\n"));
|
|
|
|
|
|
|
|
proxy_append_header(con, "X-Forwarded-For", (char *)inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
|
|
|
|
/* http_host is NOT is just a pointer to a buffer
|
|
|
|
* which is NULL if it is not set */
|
|
|
|
if (!buffer_string_is_empty(con->request.http_host)) {
|
|
|
|
proxy_set_header(con, "X-Host", con->request.http_host->ptr);
|
|
|
|
}
|
|
|
|
proxy_set_header(con, "X-Forwarded-Proto", con->uri.scheme->ptr);
|
|
|
|
|
|
|
|
/* request header */
|
|
|
|
for (i = 0; i < con->request.headers->used; i++) {
|
|
|
|
data_string *ds;
|
|
|
|
|
|
|
|
ds = (data_string *)con->request.headers->data[i];
|
|
|
|
|
|
|
|
if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
|
|
|
|
if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Connection"))) continue;
|
|
|
|
if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy-Connection"))) continue;
|
|
|
|
|
|
|
|
buffer_append_string_buffer(b, ds->key);
|
|
|
|
buffer_append_string_len(b, CONST_STR_LEN(": "));
|
|
|
|
buffer_append_string_buffer(b, ds->value);
|
|
|
|
buffer_append_string_len(b, CONST_STR_LEN("\r\n"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer_append_string_len(b, CONST_STR_LEN("Connection: close\r\n\r\n"));
|
|
|
|
|
|
|
|
hctx->wb_reqlen = buffer_string_length(b);
|
|
|
|
chunkqueue_append_buffer(hctx->wb, b);
|
|
|
|
buffer_free(b);
|
|
|
|
|
|
|
|
/* body */
|
|
|
|
|
|
|
|
if (con->request.content_length) {
|
|
|
|
chunkqueue_append_chunkqueue(hctx->wb, con->request_content_queue);
|
|
|
|
hctx->wb_reqlen += con->request.content_length;/* (eventual) total request size */
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int proxy_set_state(server *srv, handler_ctx *hctx, proxy_connection_state_t state) {
|
|
|
|
hctx->state = state;
|
|
|
|
hctx->state_timestamp = srv->cur_ts;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int proxy_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) {
|
|
|
|
char *s, *ns;
|
|
|
|
int http_response_status = -1;
|
|
|
|
|
|
|
|
UNUSED(srv);
|
|
|
|
|
|
|
|
/* [\r]\n -> [\0]\0 */
|
|
|
|
|
|
|
|
buffer_copy_buffer(p->parse_response, in);
|
|
|
|
|
|
|
|
for (s = p->parse_response->ptr; NULL != (ns = strchr(s, '\n')); s = ns + 1) {
|
|
|
|
char *key, *value;
|
|
|
|
int key_len;
|
|
|
|
data_string *ds;
|
|
|
|
int copy_header;
|
|
|
|
|
|
|
|
ns[0] = '\0';
|
|
|
|
if (s != ns && ns[-1] == '\r') ns[-1] = '\0';
|
|
|
|
|
|
|
|
if (-1 == http_response_status) {
|
|
|
|
/* The first line of a Response message is the Status-Line */
|
|
|
|
|
|
|
|
for (key=s; *key && *key != ' '; key++);
|
|
|
|
|
|
|
|
if (*key) {
|
|
|
|
http_response_status = (int) strtol(key, NULL, 10);
|
|
|
|
if (http_response_status < 100 || http_response_status >= 1000) http_response_status = 502;
|
|
|
|
} else {
|
|
|
|
http_response_status = 502;
|
|
|
|
}
|
|
|
|
|
|
|
|
con->http_status = http_response_status;
|
|
|
|
con->parsed_response |= HTTP_STATUS;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL == (value = strchr(s, ':'))) {
|
|
|
|
/* now we expect: "<key>: <value>\n" */
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
key = s;
|
|
|
|
key_len = value - key;
|
|
|
|
|
|
|
|
value++;
|
|
|
|
/* strip WS */
|
|
|
|
while (*value == ' ' || *value == '\t') value++;
|
|
|
|
|
|
|
|
copy_header = 1;
|
|
|
|
|
|
|
|
switch(key_len) {
|
|
|
|
case 4:
|
|
|
|
if (0 == strncasecmp(key, "Date", key_len)) {
|
|
|
|
con->parsed_response |= HTTP_DATE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
if (0 == strncasecmp(key, "Location", key_len)) {
|
|
|
|
con->parsed_response |= HTTP_LOCATION;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
if (0 == strncasecmp(key, "Connection", key_len)) {
|
|
|
|
copy_header = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
if (0 == strncasecmp(key, "Content-Length", key_len)) {
|
|
|
|
con->response.content_length = strtoul(value, NULL, 10);
|
|
|
|
con->parsed_response |= HTTP_CONTENT_LENGTH;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (copy_header) {
|
|
|
|
if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
|
|
|
|
ds = data_response_init();
|
|
|
|
}
|
|
|
|
buffer_copy_string_len(ds->key, key, key_len);
|
|
|
|
buffer_copy_string(ds->value, value);
|
|
|
|
|
|
|
|
array_insert_unique(con->response.headers, (data_unset *)ds);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int proxy_demux_response(server *srv, handler_ctx *hctx) {
|
|
|
|
int fin = 0;
|
|
|
|
int b;
|
|
|
|
ssize_t r;
|
|
|
|
|
|
|
|
plugin_data *p = hctx->plugin_data;
|
|
|
|
connection *con = hctx->remote_conn;
|
|
|
|
int proxy_fd = hctx->fd;
|
|
|
|
|
|
|
|
/* check how much we have to read */
|
|
|
|
if (ioctl(hctx->fd, FIONREAD, &b)) {
|
[core] option to stream response body to client (fixes #949, #760, #1283, #1387)
Set server.stream-response-body = 1 or server.stream-response-body = 2
to have lighttpd stream response body to client as it arrives from the
backend (CGI, FastCGI, SCGI, proxy).
default: buffer entire response body before sending response to client.
(This preserves existing behavior for now, but may in the future be
changed to stream response to client, which is the behavior more
commonly expected.)
x-ref:
"fastcgi, cgi, flush, php5 problem."
https://redmine.lighttpd.net/issues/949
"Random crashing on FreeBSD 6.1"
https://redmine.lighttpd.net/issues/760
"Memory usage increases when proxy+ssl+large file"
https://redmine.lighttpd.net/issues/1283
"lighttpd+fastcgi memory problem"
https://redmine.lighttpd.net/issues/1387
7 years ago
|
|
|
if (errno == EAGAIN) {
|
|
|
|
fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sd",
|
|
|
|
"ioctl failed: ",
|
|
|
|
proxy_fd);
|
|
|
|
return -1;
|
|
|
|
}< |