blob: c3a49ac3612396fcc838b4e4a29c743559966466 [file] [log] [blame]
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_string.h>
#include <ngx_file.h>
#include <ngx_hunk.h>
#include <ngx_inet.h>
#include <ngx_conf_file.h>
#include <ngx_event_write.h>
#include <ngx_event_proxy.h>
#include <ngx_http.h>
#include <ngx_http_config.h>
#include <ngx_http_core_module.h>
#include <ngx_http_output_filter.h>
#include <ngx_http_event_proxy_handler.h>
/* STUB */
typedef struct {
int dummy;
} ngx_cache_header_t;
static int ngx_http_proxy_handler(ngx_http_request_t *r);
static ngx_chain_t *ngx_http_proxy_create_request(ngx_http_proxy_ctx_t *p);
static int ngx_http_proxy_process_upstream(ngx_http_proxy_ctx_t *p,
ngx_event_t *ev);
static int ngx_http_proxy_connect(ngx_http_proxy_ctx_t *p);
static int ngx_http_proxy_process_upstream_event(ngx_event_t *ev);
static int ngx_http_proxy_send_request(ngx_http_proxy_ctx_t *p);
static int ngx_http_proxy_init_upstream(ngx_http_proxy_ctx_t *p);
static int ngx_http_proxy_read_upstream_header(ngx_http_proxy_ctx_t *p);
static int ngx_http_proxy_process_upstream_status_line(ngx_http_proxy_ctx_t *p);
static int ngx_http_proxy_process_upstream_headers(ngx_http_proxy_ctx_t *p);
static int ngx_http_proxy_process_upstream_header_line(ngx_http_proxy_ctx_t *p);
static int ngx_http_proxy_read_upstream_body(ngx_http_proxy_ctx_t *p);
static int ngx_http_proxy_write_upstream_body(ngx_http_proxy_ctx_t *p);
static int ngx_http_proxy_read_response_body(ngx_event_t *ev);
static int ngx_http_proxy_write_to_client(ngx_event_t *ev);
static int ngx_read_http_proxy_status_line(ngx_http_proxy_ctx_t *ctx);
static int ngx_http_proxy_finalize_request(ngx_http_proxy_ctx_t *p, int error);
static size_t ngx_http_proxy_log_error(void *data, char *buf, size_t len);
static int ngx_http_proxy_init(ngx_pool_t *pool);
static void *ngx_http_proxy_create_loc_conf(ngx_pool_t *pool);
static char *ngx_http_proxy_set_pass(ngx_conf_t *cf, ngx_command_t *cmd,
char *conf);
static char *ngx_http_proxy_parse_upstream(ngx_str_t *url,
ngx_http_proxy_upstream_url_t *uu);
static ngx_command_t ngx_http_proxy_commands[] = {
{ngx_string("proxy_pass"),
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_proxy_set_pass,
NGX_HTTP_LOC_CONF_OFFSET,
0},
{ngx_string("proxy_large_header"),
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, large_header)},
{ngx_null_string, 0, NULL, 0, 0}
};
static ngx_http_module_t ngx_http_proxy_module_ctx = {
NGX_HTTP_MODULE,
NULL, /* create server config */
NULL, /* init server config */
ngx_http_proxy_create_loc_conf, /* create location config */
NULL /* merge location config */
};
ngx_module_t ngx_http_proxy_module = {
0, /* module index */
&ngx_http_proxy_module_ctx, /* module context */
ngx_http_proxy_commands, /* module directives */
NGX_HTTP_MODULE_TYPE, /* module type */
ngx_http_proxy_init /* init module */
};
static ngx_str_t http_methods[] = {
ngx_string("GET "),
ngx_string("HEAD "),
ngx_string("POST ")
};
static char *header_errors[] = {
"upstream sent too long status line",
"upstream sent invalid header",
"upstream sent too long header line"
};
static ngx_http_header_t headers_in[] = {
{ ngx_string("Date"), offsetof(ngx_http_proxy_headers_in_t, date) },
{ ngx_string("Server"), offsetof(ngx_http_proxy_headers_in_t, server) },
{ ngx_string("Connection"),
offsetof(ngx_http_proxy_headers_in_t, connection) },
{ ngx_string("Content-Type"),
offsetof(ngx_http_proxy_headers_in_t, content_type) },
{ ngx_string("Content-Length"),
offsetof(ngx_http_proxy_headers_in_t, content_length) },
{ ngx_string("Last-Modified"),
offsetof(ngx_http_proxy_headers_in_t, last_modified) },
{ ngx_null_string, 0 }
};
static char http_version[] = " HTTP/1.0" CRLF;
static char host_header[] = "Host: ";
static char conn_close_header[] = "Connection: close" CRLF;
/* AF_INET only */
static int ngx_http_proxy_handler(ngx_http_request_t *r)
{
int rc;
struct sockaddr_in addr;
ngx_chain_t *chain;
ngx_http_proxy_ctx_t *p;
ngx_http_log_ctx_t *hcx;
ngx_http_proxy_log_ctx_t *lcx;
ngx_http_proxy_loc_conf_t *lcf;
p = (ngx_http_proxy_ctx_t *)
ngx_http_get_module_ctx(r, ngx_http_proxy_module_ctx);
if (p == NULL) {
ngx_http_create_ctx(r, p, ngx_http_proxy_module_ctx,
sizeof(ngx_http_proxy_ctx_t),
NGX_HTTP_INTERNAL_SERVER_ERROR);
}
if (p->upstream_url == NULL) {
lcf = (ngx_http_proxy_loc_conf_t *)
ngx_http_get_module_loc_conf(r, ngx_http_proxy_module_ctx);
p->lcf = lcf;
p->request = r;
p->upstream_url = lcf->upstream_url;
p->upstreams = lcf->upstreams;
p->tries = lcf->upstreams->number;
}
ngx_test_null(p->log, ngx_palloc(r->pool, sizeof(ngx_log_t)),
NGX_HTTP_INTERNAL_SERVER_ERROR);
ngx_memcpy(p->log, r->connection->log, sizeof(ngx_log_t));
ngx_test_null(lcx, ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_log_ctx_t)),
NGX_HTTP_INTERNAL_SERVER_ERROR);
p->log->data = lcx;
hcx = r->connection->log->data;
lcx->client = hcx->client;
lcx->url = hcx->url;
r->proxy = 1;
p->accel = 1;
p->method = r->method;
p->headers_in.headers = ngx_create_table(r->pool, 10);
/* TODO: read a client's body */
chain = ngx_http_proxy_create_request(p);
if (chain == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
/* TODO: duplicate the hunks and chain if there is backend farm */
p->request_hunks = chain;
p->last_error = NGX_HTTP_BAD_GATEWAY;
ngx_http_proxy_process_upstream(p, NULL);
/* On an error ngx_http_proxy_process_upstream() calls
ngx_http_proxy_finalize_request() so we return NGX_DONE to avoid
the additional NGX_HTTP_INTERNAL_SERVER_ERROR error
that would be generated by ngx_http_process_request() */
return NGX_DONE;
}
static ngx_chain_t *ngx_http_proxy_create_request(ngx_http_proxy_ctx_t *p)
{
int i;
size_t len;
ngx_hunk_t *hunk;
ngx_chain_t *chain;
ngx_table_elt_t *header;
ngx_http_request_t *r;
r = p->request;
len = http_methods[p->method - 1].len
+ p->upstream_url->uri.len
+ (r->uri.len - p->upstream_url->location->len)
+ r->args.len + 1 /* 1 is for "?" */
+ sizeof(http_version) - 1
+ sizeof(host_header) - 1 + p->upstream_url->host.len + 2
+ sizeof(conn_close_header) - 1
+ 2; /* 2 is for "\r\n" at the header end */
header = (ngx_table_elt_t *) r->headers_in.headers->elts;
for (i = 0; i < r->headers_in.headers->nelts; i++) {
if (&header[i] == r->headers_in.host) {
continue;
}
if (&header[i] == r->headers_in.connection) {
continue;
}
/* 2 is for ": " and 2 is for "\r\n" */
len += header[i].key.len + 2 + header[i].value.len + 2;
}
/* STUB */ len++;
ngx_test_null(hunk, ngx_create_temp_hunk(r->pool, len, 0, 0), NULL);
ngx_add_hunk_to_chain(chain, hunk, r->pool, NULL);
/* the request line */
ngx_memcpy(hunk->last, http_methods[p->method - 1].data,
http_methods[p->method - 1].len);
hunk->last += http_methods[p->method - 1].len;
ngx_memcpy(hunk->last, p->upstream_url->uri.data, p->upstream_url->uri.len);
hunk->last += p->upstream_url->uri.len;
ngx_memcpy(hunk->last, r->uri.data + p->upstream_url->location->len,
r->uri.len - p->upstream_url->location->len);
hunk->last += r->uri.len - p->upstream_url->location->len;
if (r->args.len > 0) {
*(hunk->last++) = '?';
ngx_memcpy(hunk->last, r->args.data, r->args.len);
hunk->last += r->args.len;
}
ngx_memcpy(hunk->last, http_version, sizeof(http_version) - 1);
hunk->last += sizeof(http_version) - 1;
/* the "Host" header */
ngx_memcpy(hunk->last, host_header, sizeof(host_header) - 1);
hunk->last += sizeof(host_header) - 1;
ngx_memcpy(hunk->last, p->upstream_url->host.data,
p->upstream_url->host.len);
hunk->last += p->upstream_url->host.len;
*(hunk->last++) = CR; *(hunk->last++) = LF;
/* the "Connection: close" header */
ngx_memcpy(hunk->last, conn_close_header, sizeof(conn_close_header) - 1);
hunk->last += sizeof(conn_close_header) - 1;
for (i = 0; i < r->headers_in.headers->nelts; i++) {
if (&header[i] == r->headers_in.host) {
continue;
}
if (&header[i] == r->headers_in.connection) {
continue;
}
ngx_memcpy(hunk->last, header[i].key.data, header[i].key.len);
hunk->last += header[i].key.len;
*(hunk->last++) = ':'; *(hunk->last++) = ' ';
ngx_memcpy(hunk->last, header[i].value.data, header[i].value.len);
hunk->last += header[i].value.len;
*(hunk->last++) = CR; *(hunk->last++) = LF;
ngx_log_debug(r->connection->log, "proxy: '%s: %s'" _
header[i].key.data _ header[i].value.data);
}
/* add "\r\n" at the header end */
*(hunk->last++) = CR; *(hunk->last++) = LF;
/* STUB */ *(hunk->last++) = '\0';
ngx_log_debug(r->connection->log, "PROXY:\n'%s'" _ hunk->pos);
return chain;
}
static int ngx_http_proxy_process_upstream(ngx_http_proxy_ctx_t *p,
ngx_event_t *ev)
{
int rc;
time_t now;
ngx_connection_t *c;
ngx_http_proxy_upstream_t *u;
for ( ;; ) {
if (ev == NULL) {
/* STUB: look up cached connection */
c = NULL;
if (c) {
p->cached_connection = 1;
p->connection = c;
c->write->event_handler = ngx_http_proxy_process_upstream_event;
rc = ngx_http_proxy_send_request(p);
} else {
p->cached_connection = 0;
p->connection = NULL;
rc = ngx_http_proxy_connect(p);
}
if (p->connection) {
ev = p->connection->write;
}
} else {
if (ev->timedout) {
rc = NGX_HTTP_GATEWAY_TIME_OUT;
} else if (ev->write) {
rc = p->state_write_upstream_handler(p);
} else { /* ev->read */
rc = p->state_read_upstream_handler(p);
}
}
if (rc == NGX_DONE || rc == NGX_AGAIN) {
return rc;
}
if (rc == NGX_ERROR) {
return ngx_http_proxy_finalize_request(p,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_DONE;
}
if (rc == NGX_HTTP_BAD_GATEWAY || rc == NGX_HTTP_GATEWAY_TIME_OUT
|| (rc == NGX_OK
&& p->status == NGX_HTTP_INTERNAL_SERVER_ERROR
&& p->lcf->retry_500_error))
{
if (ev) {
ngx_event_close_connection(ev);
ev = NULL;
}
if (!p->cached_connection) {
if (p->upstreams->number > 1) {
now = ngx_time();
u = &p->upstreams->u[p->cur_upstream];
/* Here is the race condition when the upstreams are shared
between threads or processes but it's not serious */
u->fails++;
u->accessed = now;
/* */
}
p->tries--;
p->last_error = rc;
}
if (p->tries == 0) {
ngx_http_proxy_finalize_request(p, p->last_error);
return NGX_ERROR;
}
p->headers_in.server->key.len = 0;
p->headers_in.connection->key.len = 0;
p->headers_in.content_type->key.len = 0;
p->headers_in.content_length->key.len = 0;
p->headers_in.last_modified->key.len = 0;
p->headers_in.headers->nelts = 0;
}
if (rc == NGX_OK) {
ngx_http_proxy_finalize_request(p, p->status);
return NGX_DONE;
}
if (rc > NGX_OK) {
ngx_http_proxy_finalize_request(p, rc);
}
return NGX_DONE;
}
}
static int ngx_http_proxy_connect(ngx_http_proxy_ctx_t *p)
{
int rc, event;
struct sockaddr_in *addr;
ngx_err_t err;
ngx_socket_t s;
ngx_event_t *rev, *wev;
ngx_connection_t *c;
ngx_http_proxy_log_ctx_t *lcx;
ngx_http_proxy_upstream_t *u;
if (p->upstreams->number > 1) {
if (p->tries == p->upstreams->number) {
/* Here is the race condition
when the upstreams are shared between threads or processes
but it should not be serious */
p->cur_upstream = p->upstreams->current++;
if (p->upstreams->current >= p->upstreams->number) {
p->upstreams->current = 0;
}
/* */
#if (NGX_MULTITHREADED || NGX_MULTIPROCESSED)
/* eliminate the sequences of the race condition */
if (p->cur_upstream >= p->upstreams->number) {
p->cur_upstream = 0;
}
#endif
}
if (p->upstreams->max_fails > 0) {
for ( ;; ) {
u = &p->upstreams->u[p->cur_upstream];
/* Here is the race condition
when the upstreams are shared between threads or processes
but it should not be serious */
if (u->fails > p->upstreams->max_fails
|| u->accessed < p->upstreams->fail_timeout)
{
break;
}
/* */
p->cur_upstream++;
if (p->cur_upstream >= p->upstreams->number) {
p->cur_upstream = 0;
}
p->tries--;
if (p->tries == 0) {
return p->last_error;
}
}
}
}
lcx = p->log->data;
lcx->action = "connecting to an upstream";
lcx->upstream = p->upstreams->u[p->cur_upstream].addr_port_name.data;
p->log->handler = ngx_http_proxy_log_error;
s = ngx_socket(AF_INET, SOCK_STREAM, IPPROTO_IP, 0);
if (s == -1) {
ngx_log_error(NGX_LOG_ALERT, p->log, ngx_socket_errno,
ngx_socket_n " failed");
return NGX_ERROR;
}
if (p->lcf->rcvbuf) {
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
(const void *) &p->lcf->rcvbuf, sizeof(int)) == -1) {
ngx_log_error(NGX_LOG_ALERT, p->log, ngx_socket_errno,
"setsockopt(SO_RCVBUF) failed");
if (ngx_close_socket(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, p->log, ngx_socket_errno,
ngx_close_socket_n " failed");
}
return NGX_ERROR;
}
}
if (ngx_nonblocking(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, p->log, ngx_socket_errno,
ngx_nonblocking_n " failed");
if (ngx_close_socket(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, p->log, ngx_socket_errno,
ngx_close_socket_n " failed");
}
return NGX_ERROR;
}
c = &ngx_connections[s];
rev = &ngx_read_events[s];
wev = &ngx_write_events[s];
ngx_memzero(c, sizeof(ngx_connection_t));
ngx_memzero(rev, sizeof(ngx_event_t));
ngx_memzero(wev, sizeof(ngx_event_t));
rev->index = wev->index = NGX_INVALID_INDEX;
rev->data = wev->data = c;
c->read = rev;
c->write = wev;
rev->first = wev->first = 1;
rev->log = wev->log = c->log = p->log;
c->fd = s;
wev->close_handler = rev->close_handler = ngx_event_close_connection;
#if !(USE_KQUEUE)
#if (HAVE_EDGE_EVENT) /* epoll */
if (ngx_event_flags & NGX_HAVE_EDGE_EVENT) {
if (ngx_edge_add_event(wev) != NGX_OK) {
return NGX_ERROR;
}
}
#endif
#endif
ngx_test_null(c->pool, ngx_create_pool(p->lcf->conn_pool_size, p->log),
NGX_ERROR);
ngx_test_null(p->sockaddr,
ngx_pcalloc(c->pool, sizeof(struct sockaddr_in)),
NGX_ERROR);
addr = (struct sockaddr_in *) p->sockaddr;
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = p->upstreams->u[p->cur_upstream].addr;
addr->sin_port = htons(p->upstreams->u[p->cur_upstream].port);
rc = connect(s, p->sockaddr, sizeof(struct sockaddr_in));
if (rc == -1) {
err = ngx_socket_errno;
if (err != NGX_EINPROGRESS) {
ngx_log_error(NGX_LOG_CRIT, p->log, err, "connect() failed");
if (ngx_close_socket(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, p->log, ngx_socket_errno,
ngx_close_socket_n " failed");
}
ngx_destroy_pool(c->pool);
return NGX_HTTP_BAD_GATEWAY;
}
}
c->data = p->request;
p->connection = c;
ngx_test_null(c->pool, ngx_create_pool(p->lcf->conn_pool_size, p->log),
NGX_ERROR);
#if (USE_KQUEUE)
if (ngx_add_event(rev, NGX_READ_EVENT, NGX_CLEAR_EVENT) != NGX_OK) {
return NGX_ERROR;
}
#else
if ((ngx_event_flags & NGX_HAVE_EDGE_EVENT) == 0) { /* not epoll */
if (ngx_event_flags & NGX_HAVE_CLEAR_EVENT) { /* kqueue */
event = NGX_CLEAR_EVENT;
} else { /* select, poll, /dev/poll */
event = NGX_LEVEL_EVENT;
}
/* TODO: aio, iocp */
if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {
return NGX_ERROR;
}
}
#endif /* USE_KQUEUE */
wev->event_handler = rev->event_handler =
ngx_http_proxy_process_upstream_event;
p->state_write_upstream_handler = ngx_http_proxy_send_request;
p->state_read_upstream_handler = ngx_http_proxy_init_upstream;
/* The connection has been established */
if (rc == 0) {
wev->write = 1;
wev->ready = 1;
return ngx_http_proxy_send_request(p);
}
/* The connection is in a progress */
wev->timer_set = 1;
ngx_add_timer(wev, p->lcf->connect_timeout);
#if (USE_KQUEUE)
if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_CLEAR_EVENT) != NGX_OK) {
return NGX_ERROR;
}
#else
/* TODO: aio, iocp */
if (ngx_event_flags & NGX_HAVE_EDGE_EVENT) {
return NGX_DONE;
}
if (ngx_add_event(wev, NGX_WRITE_EVENT, event) != NGX_OK) {
return NGX_ERROR;
}
#endif /* USE_KQUEUE */
return NGX_DONE;
}
static int ngx_http_proxy_process_upstream_event(ngx_event_t *ev)
{
ngx_connection_t *c;
ngx_http_request_t *r;
ngx_http_proxy_ctx_t *p;
c = (ngx_connection_t *) ev->data;
r = (ngx_http_request_t *) c->data;
p = (ngx_http_proxy_ctx_t *)
ngx_http_get_module_ctx(r, ngx_http_proxy_module_ctx);
return ngx_http_proxy_process_upstream(p, ev);
}
static int ngx_http_proxy_send_request(ngx_http_proxy_ctx_t *p)
{
ngx_chain_t *chain;
ngx_event_t *wev;
chain = ngx_write_chain(p->connection, p->request_hunks, 0);
if (chain == (ngx_chain_t *) -1) {
return NGX_ERROR;
}
p->request_hunks = chain;
wev = p->connection->write;
ngx_del_timer(wev);
if (chain) {
ngx_add_timer(wev, p->lcf->send_timeout);
wev->timer_set = 1;
} else {
wev->timer_set = 0;
}
return NGX_DONE;
}
static int ngx_http_proxy_init_upstream(ngx_http_proxy_ctx_t *p)
{
int n;
ngx_hunk_t **ph;
ngx_http_request_t *r;
r = p->request;
ngx_test_null(p->header_in,
ngx_create_temp_hunk(r->pool, p->lcf->header_size, 0, 0),
NGX_ERROR);
#if 0
ngx_test_null(p->header_in,
ngx_create_temp_hunk(r->pool,
p->lcf->header_size
- sizeof(ngx_cache_header_t),
sizeof(ngx_cache_header_t),
0),
NGX_ERROR);
p->header_in->type = NGX_HUNK_MEMORY|NGX_HUNK_IN_MEMORY;
#endif
#if 0
ngx_test_null(p->headers_in,
ngx_palloc(r->pool, sizeof(ngx_http_proxy_headers_in_t)),
NGX_ERROR);
#endif
p->nhunks = p->lcf->max_block_size / p->lcf->block_size;
if (p->nhunks * p->lcf->block_size < p->lcf->max_block_size) {
p->nhunks++;
}
ngx_init_array(p->hunks, r->pool, p->nhunks, sizeof(ngx_hunk_t *),
NGX_ERROR);
ngx_test_null(ph, ngx_push_array(&p->hunks), NGX_ERROR);
*ph = p->header_in;
p->state_handler = ngx_http_proxy_process_upstream_status_line;
return ngx_http_proxy_read_upstream_header(p);
}
static int ngx_http_proxy_read_upstream_header(ngx_http_proxy_ctx_t *p)
{
int i, n, rc;
ngx_event_t *rev;
ngx_chain_t *temp;
ngx_table_elt_t *ch, *ph;
ngx_event_proxy_t *ep;
ngx_http_request_t *r;
ngx_http_proxy_log_ctx_t *lcx;
rev = p->connection->read;
do {
n = ngx_event_recv(p->connection, p->header_in->last,
p->header_in->end - p->header_in->last);
if (n == NGX_AGAIN) {
if (rev->timer_set) {
ngx_del_timer(rev);
} else {
rev->timer_set = 1;
}
ngx_add_timer(rev, p->lcf->read_timeout);
return NGX_AGAIN;
}
if (n == NGX_ERROR) {
return NGX_HTTP_BAD_GATEWAY;
}
ngx_log_debug(p->log, "http proxy read %d" _ n);
if (n == 0) {
ngx_log_error(NGX_LOG_INFO, p->log, 0,
"upstream closed prematurely connection");
return NGX_HTTP_BAD_GATEWAY;
}
p->header_in->last += n;
/* the state handlers are called in the following order:
ngx_http_proxy_process_upstream_status_line(p)
ngx_http_proxy_process_upstream_headers(p) */
do {
rc = p->state_handler(p);
} while (rc == NGX_AGAIN && p->header_in->pos < p->header_in->last);
} while (rc == NGX_AGAIN
&& (rev->ready || ngx_event_flags & NGX_HAVE_AIO_EVENT));
if (rc == NGX_OK) {
r = p->request;
/* copy an upstream header to r->headers_out */
ph = (ngx_table_elt_t *) p->headers_in.headers->elts;
for (i = 0; i < p->headers_in.headers->nelts; i++) {
if (&ph[i] == p->headers_in.connection) {
continue;
}
if (p->accel && &ph[i] == p->headers_in.date) {
continue;
}
ngx_test_null(ch, ngx_push_table(r->headers_out.headers),
NGX_HTTP_INTERNAL_SERVER_ERROR);
ch->key.len = ph[i].key.len;
ch->key.data = ph[i].key.data;
ch->value.len = ph[i].value.len;
ch->value.data = ph[i].value.data;
}
if (p->headers_in.server) {
r->headers_out.server = p->headers_in.server;
}
if (!p->accel && p->headers_in.date) {
r->headers_out.date = p->headers_in.date;
}
/* TODO: look "Content-Length" */
p->block_size = p->lcf->block_size;
r->headers_out.status = p->status;
rc = ngx_http_send_header(r);
#if 1
#if 0
rc = ngx_http_output_filter(r, p->header_in);
#endif
ngx_test_null(ep, ngx_pcalloc(r->pool, sizeof(ngx_event_proxy_t)),
NGX_ERROR);
ep->output_filter = (ngx_event_proxy_output_filter_pt)
ngx_http_output_filter;
ep->output_data = r;
ep->block_size = p->lcf->block_size;
ep->max_block_size = p->lcf->max_block_size;
ep->upstream = p->connection;
ep->downstream = r->connection;
ep->pool = r->pool;
ep->log = p->log;
ep->temp_path = p->lcf->temp_path;
ngx_test_null(ep->temp_file, ngx_palloc(r->pool, sizeof(ngx_file_t)),
NGX_ERROR);
ep->temp_file->fd = NGX_INVALID_FILE;
ep->temp_file->log = p->log;
ep->number = 10;
ep->random = 5;
ep->max_temp_file_size = p->lcf->max_temp_file_size;
ep->temp_file_write_size = p->lcf->temp_file_write_size;
ep->temp_file_warn = "an upstream response is buffered "
"to a temporary file";
ngx_test_null(ep->preread_hunks, ngx_alloc_chain_entry(r->pool),
NGX_ERROR);
ep->preread_hunks->hunk = p->header_in;
ep->preread_hunks->next = NULL;
#if 0
ep->last_preread_hunk = ep->preread_hunks;
#endif
ep->preread_size = p->header_in->last - p->header_in->pos;
p->event_proxy = ep;
lcx = p->log->data;
lcx->action = "reading an upstream";
p->state_read_upstream_handler = ngx_http_proxy_read_upstream_body;
p->state_write_upstream_handler = ngx_http_proxy_write_upstream_body;
ngx_http_proxy_read_upstream_body(p);
#endif
#if 0
/* STUB */
p->header_in->type |= NGX_HUNK_LAST;
rc = ngx_http_output_filter(r, p->header_in);
ngx_http_proxy_finalize_request(p, NGX_OK);
#endif
/* STUB */ return NGX_DONE;
}
if (rc > NGX_OK) {
return rc;
}
/* STUB */ return NGX_DONE;
}
static int ngx_http_proxy_process_upstream_status_line(ngx_http_proxy_ctx_t *p)
{
int rc;
rc = ngx_read_http_proxy_status_line(p);
if (rc == NGX_HTTP_PROXY_PARSE_NO_HEADER) {
p->status = 200;
p->status_line.len = 0;
p->full_status_line.len = 0;
p->state_handler = ngx_http_proxy_process_upstream_headers;
} else if (rc == NGX_OK) {
p->status_line.len = p->status_end - p->status_start;
p->full_status_line.len = p->status_end - p->header_in->start;
if (p->lcf->large_header) {
ngx_test_null(p->full_status_line.data,
ngx_palloc(p->request->pool,
p->full_status_line.len + 1),
NGX_HTTP_INTERNAL_SERVER_ERROR);
ngx_cpystrn(p->full_status_line.data, p->header_in->start,
p->full_status_line.len + 1);
if (p->header_in->pos == p->header_in->end) {
p->header_in->pos = p->header_in->last = p->header_in->start;
}
} else {
p->status_line.data = p->status_start;
p->full_status_line.data = p->header_in->start;
*p->status_end = '\0';
if (p->header_in->pos == p->header_in->end) {
ngx_log_error(NGX_LOG_ERR, p->log, 0,
"upstream sent too long status line");
return NGX_HTTP_BAD_GATEWAY;
}
}
ngx_log_debug(p->log, "upstream status: %d, '%s'" _
p->status _ p->full_status_line.data);
p->state_handler = ngx_http_proxy_process_upstream_headers;
}
/* rc == NGX_AGAIN */
return NGX_AGAIN;
}
static int ngx_http_proxy_process_upstream_headers(ngx_http_proxy_ctx_t *p)
{
int rc, offset;
ngx_http_request_t *r;
r = p->request;
for ( ;; ) {
rc = ngx_read_http_header_line(r, p->header_in);
/* a header line has been parsed successfully */
if (rc == NGX_OK) {
if (ngx_http_proxy_process_upstream_header_line(p) == NGX_ERROR) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (p->lcf->large_header
&& p->header_in->pos == p->header_in->last)
{
p->header_in->pos = p->header_in->last = p->header_in->start;
}
return NGX_AGAIN;
/* a whole header has been parsed successfully */
} else if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
ngx_log_debug(p->log, "proxy HTTP header done");
p->state_handler = NULL;
return NGX_OK;
/* there was error while a header line parsing */
} else if (rc != NGX_AGAIN) {
ngx_log_error(NGX_LOG_ERR, p->log, 0,
"upstream sent ERROR %d", rc);
return NGX_HTTP_BAD_GATEWAY;
}
/* NGX_AGAIN: a header line parsing is still not complete */
if (p->header_in->pos == p->header_in->last) {
/* if the large upstream headers are enabled then
we need to compact p->header_in hunk */
if (p->lcf->large_header) {
offset = r->header_name_start - p->header_in->start;
if (offset == 0) {
ngx_log_error(NGX_LOG_ERR, p->log, 0,
"upstream sent too long header line");
return NGX_HTTP_BAD_GATEWAY;
}
ngx_memcpy(p->header_in->start, r->header_name_start,
p->header_in->last - r->header_name_start);
p->header_in->last -= offset;
p->header_in->pos -= offset;
r->header_name_start = p->header_in->start;
r->header_name_end -= offset;
r->header_start -= offset;
r->header_end -= offset;
} else {
ngx_log_error(NGX_LOG_ERR, p->log, 0,
"upstream sent too long header line");
/* NGX_HTTP_PARSE_TOO_LONG_HEADER */
return NGX_HTTP_BAD_GATEWAY;
}
}
return NGX_AGAIN;
}
}
static int ngx_http_proxy_process_upstream_header_line(ngx_http_proxy_ctx_t *p)
{
int i;
ngx_table_elt_t *h;
ngx_http_request_t *r;
r = p->request;
ngx_test_null(h, ngx_push_table(p->headers_in.headers), NGX_ERROR);
h->key.len = r->header_name_end - r->header_name_start;
h->value.len = r->header_end - r->header_start;
/* if the large upstream headers are enabled then
we need to copy the header name and value */
if (p->lcf->large_header) {
ngx_test_null(h->key.data, ngx_palloc(r->pool, h->key.len + 1),
NGX_ERROR);
ngx_test_null(h->value.data, ngx_palloc(r->pool, h->value.len + 1),
NGX_ERROR);
ngx_cpystrn(h->key.data, r->header_name_start, h->key.len + 1);
ngx_cpystrn(h->value.data, r->header_start, h->value.len + 1);
} else {
h->key.data = r->header_name_start;
h->key.data[h->key.len] = '\0';
h->value.data = r->header_start;
h->value.data[h->value.len] = '\0';
}
for (i = 0; headers_in[i].name.len != 0; i++) {
if (headers_in[i].name.len != h->key.len) {
continue;
}
if (ngx_strcasecmp(headers_in[i].name.data, h->key.data) == 0) {
*((ngx_table_elt_t **)
((char *) &p->headers_in + headers_in[i].offset)) = h;
}
}
ngx_log_debug(p->log, "proxy HTTP header: '%s: %s'" _
h->key.data _ h->value.data);
return NGX_OK;
}
static int ngx_http_proxy_read_upstream_body(ngx_http_proxy_ctx_t *p)
{
int rc;
rc = ngx_event_proxy_read_upstream(p->event_proxy);
if (rc == NGX_OK) {
rc = ngx_event_close_connection(p->connection->read);
}
return rc;
}
static int ngx_http_proxy_write_upstream_body(ngx_http_proxy_ctx_t *p)
{
return ngx_event_proxy_write_to_downstream(p->event_proxy);
}
#if 0
static int ngx_http_proxy_read_upstream_body(ngx_event_t *rev)
{
int rc, n, size, eof;
ngx_hunk_t *h;
ngx_chain_t *chain, *ce, *tce;
ngx_event_t *wev;
ngx_connection_t *c;
ngx_http_request_t *r;
ngx_http_proxy_ctx_t *p;
c = (ngx_connection_t *) rev->data;
r = (ngx_http_request_t *) c->data;
p = (ngx_http_proxy_ctx_t *)
ngx_http_get_module_ctx(r, ngx_http_proxy_module_ctx);
eof = 0;
for ( ;; ) {
#if (USE_KQUEUE)
if (ev->eof && ev->available == 0) {
eof = 1;
break;
}
#elif (HAVE_KQUEUE0)
if (ngx_event_type == NGX_HAVE_KQUEUE_EVENT
&& ev->eof && ev->available == 0)
{
eof = 1;
break;
}
#endif
/* use the free hunks if they exist */
if (p->free_hunks) {
chain = p->free_hunks;
p->free_hunks = NULL;
/* allocate a new hunk if it's still allowed */
} else if (p->allocated < p->lcf->max_block_size) {
ngx_test_null(h,
ngx_create_temp_hunk(r->pool, p->block_size, 50, 50),
NGX_ERROR);
p->allocated += p->block_size;
ngx_test_null(tce, ngx_create_chain_entry(r->pool), NGX_ERROR);
tce->hunk = h;
tce->next = NULL;
chain = tce;
/* use the shadow hunks if they exist */
} else if (p->shadow_hunks) {
chain = p->shadow_hunks;
p->shadow_hunks = NULL;
/* write all the incoming hunks or the first hunk only
to a temporary file and convert them to the shadow hunks */
} else {
if (r->cachable) {
rc = ngx_http_proxy_write_chain_to_temp_file(p);
if (rc != NGX_OK) {
return rc;
}
} else {
tce = p->in_hunks->next;
p->in_hunks->next = NULL;
rc = ngx_http_proxy_write_chain_to_temp_file(p);
if (rc != NGX_OK) {
p->in_hunks = tce;
return rc;
}
p->in_hunks = tce;
}
}
n = ngx_recv_chain(c, chain);
if (n == NGX_ERROR) {
return NGX_ERROR;
}
if (n == NGX_AGAIN) {
return NGX_AGAIN;
}
if (n == 0) {
eof = 1;
break;
}
for (ce = chain; ce && n > 0; ce = ce->next) {
ngx_test_null(tce, ngx_create_chain_entry(r->pool), NGX_ERROR);
tce->hunk = ce->hunk;
tce->next = NULL;
if (p->last_in_hunk) {
p->last_in_hunk->next = tce;
p->last_in_hunk = tce;
} else {
p->last_in_hunk = tce;
}
size = ce->hunk->end - ce->hunk->last;
if (n >= size) {
n -= size;
ce->hunk->last = ce->hunk->end;
if (ce->hunk->shadow) {
ce->hunk->shadow->type &= ~(NGX_HUNK_TEMP
|NGX_HUNK_IN_MEMORY
|NGX_HUNK_RECYCLED);
ce->hunk->shadow->shadow = NULL;
}
continue;
}
ce->hunk->last += n;
if (ce->hunk->shadow) {
ce->hunk->shadow->type &= ~(NGX_HUNK_TEMP
|NGX_HUNK_IN_MEMORY
|NGX_HUNK_RECYCLED);
ce->hunk->shadow->shadow = NULL;
}
break;
}
if (ce) {
ce->next = p->free_hunks;
p->free_hunks = ce;
break;
}
}
wev = p->request->connection->write;
if (r->cachable) {
if (p->in_hunks) {
rc = ngx_http_proxy_write_chain_to_temp_file(p);
if (rc != NGX_OK) {
return rc;
}
}
if (p->out_hunks && wev->ready) {
return ngx_http_proxy_write_upstream_body(wev);
}
} else {
if ((p->out_hunks || p->in_hunks) && wev->ready) {
return ngx_http_proxy_write_upstream_body(wev);
}
}
return NGX_OK;
}
static int ngx_http_proxy_write_chain_to_temp_file(ngx_http_proxy_ctx_t *p)
{
int i, rc;
ngx_hunk_t *h;
ngx_chain_t *ce, *tce;
if (p->temp_file->fd == NGX_INVALID_FILE) {
rc = ngx_create_temp_file(p->temp_file, p->lcf->temp_path,
p->request->pool,
0, 2,
p->request->cachable);
if (rc != NGX_OK) {
return rc;
}
if (p->lcf->temp_file_warn) {
ngx_log_error(NGX_LOG_WARN, p->log, 0,
"an upstream response is buffered "
"to a temporary file");
}
}
if (ngx_write_chain_to_file(p->temp_file, p->in_hunks,
p->temp_offset, p->request->pool) == NGX_ERROR) {
return NGX_ERROR;
}
for (ce = p->in_hunks; ce; ce = ce->next) {
ngx_test_null(h, ngx_pcalloc(p->request->pool, sizeof(ngx_hunk_t)),
NGX_ERROR);
h->type = NGX_HUNK_FILE
|NGX_HUNK_TEMP|NGX_HUNK_IN_MEMORY|NGX_HUNK_RECYCLED;
ce->hunk->shadow = h;
h->shadow = ce->hunk;
h->file_pos = p->temp_offset;
p->temp_offset += ce->hunk->last - ce->hunk->pos;
h->file_last = p->temp_offset;
h->file->fd = p->temp_file->fd;
h->file->log = p->log;
h->pos = ce->hunk->pos;
h->last = ce->hunk->last;
h->start = ce->hunk->start;
h->end = ce->hunk->end;
h->pre_start = ce->hunk->pre_start;
h->post_end = ce->hunk->post_end;
ngx_test_null(tce, ngx_create_chain_entry(p->request->pool), NGX_ERROR);
tce->hunk = h;
tce->next = NULL;
if (p->last_out_hunk) {
p->last_out_hunk->next = tce;
p->last_out_hunk = tce;
} else {
p->last_out_hunk = tce;
}
}
return NGX_OK;
}
static int ngx_http_proxy_write_upstream_body(ngx_event_t *wev)
{
int rc;
ngx_hunk_t *h, *sh;
ngx_chain_t *ce;
ngx_connection_t *c;
ngx_http_request_t *r;
ngx_http_proxy_ctx_t *p;
c = (ngx_connection_t *) wev->data;
r = (ngx_http_request_t *) c->data;
p = (ngx_http_proxy_ctx_t *)
ngx_http_get_module_ctx(r, ngx_http_proxy_module_ctx);
while (p->out_hunks) {
h = p->out_hunks->hunk;
rc = ngx_http_output_filter(r, h);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_AGAIN || h->pos < h->last) {
return NGX_AGAIN;
}
p->out_hunks = p->out_hunks->next;
/* if the complete hunk has a shadow hunk
then add a shadow hunk to p->free_hunks chain */
sh = h->shadow;
if (sh) {
sh->pos = sh->last = sh->start;
ngx_test_null(ce, ngx_create_chain_entry(r->pool), NGX_ERROR);
ce->hunk = sh;
ce->next = p->free_hunks;
p->free_hunks = ce;
}
}
return NGX_OK;
}
#endif
static int ngx_http_proxy_read_response_body(ngx_event_t *ev)
{
int n;
char *buf;
size_t left, size;
ngx_hunk_t *h, **ph;
ngx_connection_t *c;
ngx_http_request_t *r;
ngx_http_proxy_ctx_t *p;
if (ev->timedout) {
return NGX_ERROR;
}
c = (ngx_connection_t *) ev->data;
r = (ngx_http_request_t *) c->data;
p = (ngx_http_proxy_ctx_t *)
ngx_http_get_module_ctx(r, ngx_http_proxy_module_ctx);
if (p->hunks.nelts > 0) {
h = ((ngx_hunk_t **) p->hunks.elts)[p->hunks.nelts - 1];
left = h->end - h->last;
} else {
h = NULL;
left = 0;
}
do {
#if (USE_KQUEUE)
/* do not allocate new block if there is EOF */
if (ev->eof && ev->available == 0) {
left = 1;
}
#elif (HAVE_KQUEUE)
if (ngx_event_type == NGX_HAVE_KQUEUE_EVENT) {
/* do not allocate new block if there is EOF */
if (ev->eof && ev->available == 0) {
left = 1;
}
}
#endif
if (left == 0) {
ngx_test_null(ph, ngx_push_array(&p->hunks), NGX_ERROR);
ngx_test_null(h,
ngx_create_temp_hunk(r->pool,
/* STUB */ 4096 /**/, 0, 0),
NGX_ERROR);
h->type = NGX_HUNK_MEMORY|NGX_HUNK_IN_MEMORY;
*ph = h;
}
if (h != NULL) {
buf = h->last;
size = h->end - h->last;
} else {
buf = (char *) &buf;
size = 0;
}
n = ngx_event_recv(c, buf, size);
ngx_log_debug(c->log, "READ:%d" _ n);
if (n == NGX_AGAIN) {
return NGX_DONE;
}
if (n == NGX_ERROR) {
return NGX_ERROR;
}
h->last += n;
left = h->end - h->last;
/* STUB */
*h->last = '\0';
ngx_log_debug(c->log, "PROXY:\n'%s'" _ h->pos);
/**/
} while (n > 0 && left == 0);
if (n == 0) {
ngx_log_debug(c->log, "CLOSE proxy");
#if 0
ngx_del_event(ev, NGX_READ_EVENT, NGX_CLOSE_EVENT);
#endif
ngx_event_close_connection(ev);
p->hunk_n = 0;
c->write->event_handler = ngx_http_proxy_write_to_client;
return ngx_http_proxy_write_to_client(c->write);
}
/* STUB */ return NGX_DONE;
}
static int ngx_http_proxy_write_to_client(ngx_event_t *ev)
{
int rc;
ngx_hunk_t *h;
ngx_connection_t *c;
ngx_http_request_t *r;
ngx_http_proxy_ctx_t *p;
c = (ngx_connection_t *) ev->data;
r = (ngx_http_request_t *) c->data;
p = (ngx_http_proxy_ctx_t *)
ngx_http_get_module_ctx(r, ngx_http_proxy_module_ctx);
do {
h = ((ngx_hunk_t **) p->hunks.elts)[p->hunk_n];
rc = ngx_http_output_filter(r, h);
if (rc != NGX_OK) {
return rc;
}
if (p->hunk_n >= p->hunks.nelts) {
break;
}
p->hunk_n++;
} while (rc == NGX_OK);
return NGX_OK;
}
static int ngx_http_proxy_finalize_request(ngx_http_proxy_ctx_t *p, int error)
{
return ngx_http_finalize_request(p->request, error);
}
static int ngx_http_proxy_error(ngx_http_request_t *r, ngx_http_proxy_ctx_t *p,
int error)
{
ngx_event_close_connection(p->connection->read);
return ngx_http_error(r, error);
}
static size_t ngx_http_proxy_log_error(void *data, char *buf, size_t len)
{
ngx_http_proxy_log_ctx_t *lcx = (ngx_http_proxy_log_ctx_t *) data;
return ngx_snprintf(buf, len,
" while %s, upstream: %s, client: %s, URL: %s",
lcx->action, lcx->upstream, lcx->client, lcx->url);
}
static int ngx_read_http_proxy_status_line(ngx_http_proxy_ctx_t *ctx)
{
char ch;
char *p;
enum {
sw_start = 0,
sw_first_major_digit,
sw_major_digit,
sw_first_minor_digit,
sw_minor_digit,
sw_status,
sw_space_after_status,
sw_status_text,
sw_almost_done,
sw_done
} state;
state = ctx->state;
p = ctx->header_in->pos;
while (p < ctx->header_in->last && state < sw_done) {
ch = *p++;
switch (state) {
/* "HTTP/" */
case sw_start:
if (p + 3 >= ctx->header_in->last) {
return NGX_AGAIN;
}
if (ch != 'H' || *p != 'T' || *(p + 1) != 'T' || *(p + 2) != 'P'
|| *(p + 3) != '/')
{
return NGX_HTTP_PROXY_PARSE_NO_HEADER;
}
p += 4;
state = sw_first_major_digit;
break;
/* first digit of major HTTP version */
case sw_first_major_digit:
if (ch < '1' || ch > '9') {
return NGX_HTTP_PROXY_PARSE_NO_HEADER;
}
state = sw_major_digit;
break;
/* major HTTP version or dot */
case sw_major_digit:
if (ch == '.') {
state = sw_first_minor_digit;
break;
}
if (ch < '0' || ch > '9') {
return NGX_HTTP_PROXY_PARSE_NO_HEADER;
}
break;
/* first digit of minor HTTP version */
case sw_first_minor_digit:
if (ch < '0' || ch > '9') {
return NGX_HTTP_PROXY_PARSE_NO_HEADER;
}
state = sw_minor_digit;
break;
/* minor HTTP version or end of request line */
case sw_minor_digit:
if (ch == ' ') {
state = sw_status;
break;
}
if (ch < '0' || ch > '9') {
return NGX_HTTP_PROXY_PARSE_NO_HEADER;
}
break;
/* HTTP status code */
case sw_status:
if (ch < '0' || ch > '9') {
return NGX_HTTP_PROXY_PARSE_NO_HEADER;
}
ctx->status = ctx->status * 10 + ch - '0';
if (++ctx->status_count == 3) {
state = sw_space_after_status;
ctx->status_start = p - 3;
}
break;
/* space or end of line */
case sw_space_after_status:
switch (ch) {
case ' ':
state = sw_status_text;
break;
case CR:
state = sw_almost_done;
break;
case LF:
state = sw_done;
break;
default:
return NGX_HTTP_PROXY_PARSE_NO_HEADER;
}
break;
/* any text until end of line */
case sw_status_text:
switch (ch) {
case CR:
state = sw_almost_done;
break;
case LF:
state = sw_done;
break;
}
break;
/* end of request line */
case sw_almost_done:
ctx->status_end = p - 2;
switch (ch) {
case LF:
state = sw_done;
break;
default:
return NGX_HTTP_PROXY_PARSE_NO_HEADER;
}
break;
}
}
ctx->header_in->pos = p;
if (state == sw_done) {
if (ctx->status_end == NULL) {
ctx->status_end = p - 1;
}
ctx->state = sw_start;
return NGX_OK;
} else {
ctx->state = state;
return NGX_AGAIN;
}
}
static int ngx_http_proxy_init(ngx_pool_t *pool)
{
int i;
ngx_file_t file;
ngx_path_t path;
file.log = pool->log;
path.name.data = "temp";
path.name.len = 4;
path.level[0] = 1;
path.level[1] = 2;
path.level[2] = 3;
path.len = 0;
for (i = 0; i < 3; i++) {
if (path.level[i] == 0) {
break;
}
path.len += path.level[i] + 1;
}
return ngx_create_temp_file(&file, &path, pool, 123456789, 2, 0);
}
static void *ngx_http_proxy_create_loc_conf(ngx_pool_t *pool)
{
int i;
ngx_http_proxy_loc_conf_t *conf;
ngx_test_null(conf,
ngx_pcalloc(pool, sizeof(ngx_http_proxy_loc_conf_t)),
NULL);
/* STUB */
conf->conn_pool_size = 16384;
conf->connect_timeout = 10000;
conf->send_timeout = 10000;
conf->read_timeout = 10000;
conf->header_size = 2048;
#if 1
conf->block_size = 4096;
conf->max_block_size = 4096 * 3;
conf->max_temp_file_size = 4096 * 5;
conf->temp_file_write_size = 4096 * 2;
#else
conf->block_size = 2048;
conf->max_block_size = 4096 * 6;
conf->max_temp_file_size = 4096 * 5;
conf->temp_file_write_size = 4096 * 5;
#endif
ngx_test_null(conf->temp_path, ngx_pcalloc(pool, sizeof(ngx_path_t)), NULL);
conf->temp_path->name.data = "temp";
conf->temp_path->name.len = 4;
conf->temp_path->level[0] = 1;
conf->temp_path->level[1] = 2;
conf->temp_path->level[2] = 3;
conf->temp_path->len = 0;
for (i = 0; i < 3; i++) {
if (conf->temp_path->level[i] == 0) {
break;
}
conf->temp_path->len += conf->temp_path->level[i] + 1;
}
/**/
return conf;
}
static char *ngx_http_proxy_set_pass(ngx_conf_t *cf, ngx_command_t *cmd,
char *conf)
{
ngx_http_proxy_loc_conf_t *lcf = (ngx_http_proxy_loc_conf_t *) conf;
int i, s, len;
char *err, *host;
struct hostent *h;
u_int32_t addr;
ngx_str_t *value;
ngx_http_conf_ctx_t *ctx;
ngx_http_core_loc_conf_t *core_lcf;
value = (ngx_str_t *) cf->args->elts;
if (ngx_strncasecmp(value[1].data, "http://", 7) != 0) {
return "invalid URL prefix";
}
ngx_test_null(lcf->upstream_url,
ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_upstream_url_t)),
NGX_CONF_ERROR);
value[1].data += 7;
value[1].len -= 7;
err = ngx_http_proxy_parse_upstream(&value[1], lcf->upstream_url);
if (err) {
return err;
}
if (lcf->upstream_url->port == 0) {
lcf->upstream_url->port = 80;
}
ngx_test_null(host, ngx_palloc(cf->pool, lcf->upstream_url->host.len + 1),
NGX_CONF_ERROR);
ngx_cpystrn(host, lcf->upstream_url->host.data,
lcf->upstream_url->host.len + 1);
/* TODO: look up upstreams */
addr = inet_addr(host);
if (addr == INADDR_NONE) {
h = gethostbyname(host);
if (h == NULL || h->h_addr_list[0] == NULL) {
/* STUB: "host %s not found", host */
return "host not found";
}
for (i = 0; h->h_addr_list[i] != NULL; i++) {
/* void */
}
/* MP: ngx_shared_palloc() */
ngx_test_null(lcf->upstreams,
ngx_pcalloc(cf->pool,
sizeof(ngx_http_proxy_upstreams_t)
+ sizeof(ngx_http_proxy_upstream_t) * (i - 1)),
NGX_CONF_ERROR);
lcf->upstreams->number = i;
for (i = 0; h->h_addr_list[i] != NULL; i++) {
lcf->upstreams->u[i].host.data = host;
lcf->upstreams->u[i].host.len = lcf->upstream_url->host.len;
lcf->upstreams->u[i].addr = *(u_int32_t *)(h->h_addr_list[i]);
lcf->upstreams->u[i].port = lcf->upstream_url->port;
len = INET_ADDRSTRLEN + lcf->upstream_url->port_name.len + 1;
ngx_test_null(lcf->upstreams->u[i].addr_port_name.data,
ngx_palloc(cf->pool, len),
NGX_CONF_ERROR);
s = ngx_inet_ntop(AF_INET,
(char *) &lcf->upstreams->u[i].addr,
lcf->upstreams->u[i].addr_port_name.data,
len);
lcf->upstreams->u[i].addr_port_name.data[s++] = ':';
ngx_cpystrn(lcf->upstreams->u[i].addr_port_name.data + s,
lcf->upstream_url->port_name.data,
lcf->upstream_url->port_name.len + 1);
lcf->upstreams->u[i].addr_port_name.len =
s + lcf->upstream_url->port_name.len + 1;
}
} else {
/* MP: ngx_shared_palloc() */
ngx_test_null(lcf->upstreams,
ngx_palloc(cf->pool, sizeof(ngx_http_proxy_upstreams_t)),
NGX_CONF_ERROR);
lcf->upstreams->number = 1;
lcf->upstreams->u[0].host.data = host;
lcf->upstreams->u[0].host.len = lcf->upstream_url->host.len;
lcf->upstreams->u[0].addr = addr;
lcf->upstreams->u[0].port = lcf->upstream_url->port;
len = lcf->upstream_url->host.len
+ lcf->upstream_url->port_name.len + 1;
ngx_test_null(lcf->upstreams->u[0].addr_port_name.data,
ngx_palloc(cf->pool, len + 1),
NGX_CONF_ERROR);
ngx_memcpy(lcf->upstreams->u[0].addr_port_name.data,
lcf->upstream_url->host.data,
lcf->upstream_url->host.len);
s = lcf->upstream_url->host.len;
lcf->upstreams->u[0].addr_port_name.data[s++] = ':';
ngx_cpystrn(lcf->upstreams->u[0].addr_port_name.data + s,
lcf->upstream_url->port_name.data,
lcf->upstream_url->port_name.len + 1);
lcf->upstreams->u[0].addr_port_name.len = len;
}
ctx = cf->ctx;
core_lcf = ctx->loc_conf[ngx_http_core_module_ctx.index];
core_lcf->handler = ngx_http_proxy_handler;
lcf->upstream_url->location = &core_lcf->name;
return NULL;
}
static char *ngx_http_proxy_parse_upstream(ngx_str_t *url,
ngx_http_proxy_upstream_url_t *uu)
{
size_t i;
if (url->data[0] == ':' || url->data[0] == '/') {
return "invalid upstream URL";
}
uu->host.data = url->data;
uu->host_header.data = url->data;
for (i = 1; i < url->len; i++) {
if (url->data[i] == ':') {
uu->port_name.data = &url->data[i] + 1;
uu->host.len = i;
}
if (url->data[i] == '/') {
uu->uri.data = &url->data[i];
uu->uri.len = url->len - i;
uu->host_header.len = i;
if (uu->host.len == 0) {
uu->host.len = i;
}
if (uu->port_name.data == NULL) {
return NULL;
}
uu->port_name.len = &url->data[i] - uu->port_name.data;
if (uu->port_name.len > 0) {
uu->port = ngx_atoi(uu->port_name.data, uu->port_name.len);
if (uu->port > 0) {
return NULL;
}
}
return "invalid port in upstream URL";
}
}
if (uu->host.len == 0) {
uu->host.len = i;
}
uu->host_header.len = i;
uu->uri.data = "/";
uu->uri.len = 1;
if (uu->port_name.data == NULL) {
return NULL;
}
uu->port_name.len = &url->data[i] - uu->port_name.data;
if (uu->port_name.len > 0) {
uu->port = ngx_atoi(uu->port_name.data, uu->port_name.len);
if (uu->port > 0) {
return NULL;
}
}
return "invalid port in upstream URL";
}