summaryrefslogtreecommitdiffstats
path: root/usr.sbin/nginx/src/http/ngx_http_upstream.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/nginx/src/http/ngx_http_upstream.c')
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_upstream.c441
1 files changed, 415 insertions, 26 deletions
diff --git a/usr.sbin/nginx/src/http/ngx_http_upstream.c b/usr.sbin/nginx/src/http/ngx_http_upstream.c
index dc1283a205a..45e2eb7b961 100644
--- a/usr.sbin/nginx/src/http/ngx_http_upstream.c
+++ b/usr.sbin/nginx/src/http/ngx_http_upstream.c
@@ -46,6 +46,16 @@ static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static void ngx_http_upstream_send_response(ngx_http_request_t *r,
ngx_http_upstream_t *u);
+static void ngx_http_upstream_upgrade(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r);
+static void ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r);
+static void ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
+ ngx_uint_t from_upstream, ngx_uint_t do_write);
static void
ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r);
static void
@@ -134,6 +144,9 @@ static char *ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy);
static char *ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
+static ngx_addr_t *ngx_http_upstream_get_local(ngx_http_request_t *r,
+ ngx_http_upstream_local_t *local);
+
static void *ngx_http_upstream_create_main_conf(ngx_conf_t *cf);
static char *ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf);
@@ -427,6 +440,13 @@ ngx_http_upstream_init(ngx_http_request_t *r)
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http init upstream, client timer: %d", c->read->timer_set);
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream) {
+ ngx_http_upstream_init_request(r);
+ return;
+ }
+#endif
+
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
@@ -507,7 +527,7 @@ ngx_http_upstream_init_request(ngx_http_request_t *r)
return;
}
- u->peer.local = u->conf->local;
+ u->peer.local = ngx_http_upstream_get_local(r, u->conf->local);
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
@@ -1005,6 +1025,12 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,
return;
}
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream) {
+ return;
+ }
+#endif
+
#if (NGX_HAVE_KQUEUE)
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
@@ -1111,8 +1137,6 @@ ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)
r->connection->log->action = "connecting to upstream";
- r->connection->single_connection = 0;
-
if (u->state && u->state->response_sec) {
tp = ngx_timeofday();
u->state->response_sec = tp->sec - u->state->response_sec;
@@ -1330,6 +1354,7 @@ ngx_http_upstream_reinit(ngx_http_request_t *r, ngx_http_upstream_t *u)
}
u->keepalive = 0;
+ u->upgrade = 0;
ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
u->headers_in.content_length_n = -1;
@@ -2081,6 +2106,11 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
return;
}
+ if (u->upgrade) {
+ ngx_http_upstream_upgrade(r, u);
+ return;
+ }
+
c = r->connection;
if (r->header_only) {
@@ -2364,6 +2394,284 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
static void
+ngx_http_upstream_upgrade(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ int tcp_nodelay;
+ ngx_connection_t *c;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ /* TODO: prevent upgrade if not requested or not possible */
+
+ r->keepalive = 0;
+ c->log->action = "proxying upgraded connection";
+
+ u->read_event_handler = ngx_http_upstream_upgraded_read_upstream;
+ u->write_event_handler = ngx_http_upstream_upgraded_write_upstream;
+ r->read_event_handler = ngx_http_upstream_upgraded_read_downstream;
+ r->write_event_handler = ngx_http_upstream_upgraded_write_downstream;
+
+ if (clcf->tcp_nodelay) {
+ tcp_nodelay = 1;
+
+ if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp_nodelay");
+
+ if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
+ (const void *) &tcp_nodelay, sizeof(int)) == -1)
+ {
+ ngx_connection_error(c, ngx_socket_errno,
+ "setsockopt(TCP_NODELAY) failed");
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ c->tcp_nodelay = NGX_TCP_NODELAY_SET;
+ }
+
+ if (u->peer.connection->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, u->peer.connection->log, 0,
+ "tcp_nodelay");
+
+ if (setsockopt(u->peer.connection->fd, IPPROTO_TCP, TCP_NODELAY,
+ (const void *) &tcp_nodelay, sizeof(int)) == -1)
+ {
+ ngx_connection_error(u->peer.connection, ngx_socket_errno,
+ "setsockopt(TCP_NODELAY) failed");
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ u->peer.connection->tcp_nodelay = NGX_TCP_NODELAY_SET;
+ }
+ }
+
+ if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (u->peer.connection->read->ready
+ || u->buffer.pos != u->buffer.last)
+ {
+ ngx_http_upstream_process_upgraded(r, 1, 1);
+ }
+
+ if (c->read->ready
+ || r->header_in->pos != r->header_in->last)
+ {
+ ngx_http_upstream_process_upgraded(r, 0, 1);
+ }
+}
+
+
+static void
+ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r)
+{
+ ngx_http_upstream_process_upgraded(r, 0, 0);
+}
+
+
+static void
+ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r)
+{
+ ngx_http_upstream_process_upgraded(r, 1, 1);
+}
+
+
+static void
+ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_http_upstream_process_upgraded(r, 1, 0);
+}
+
+
+static void
+ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_http_upstream_process_upgraded(r, 0, 1);
+}
+
+
+static void
+ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
+ ngx_uint_t from_upstream, ngx_uint_t do_write)
+{
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_connection_t *c, *downstream, *upstream, *dst, *src;
+ ngx_http_upstream_t *u;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+ u = r->upstream;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream process upgraded, fu:%ui", from_upstream);
+
+ downstream = c;
+ upstream = u->peer.connection;
+
+ if (downstream->write->timedout) {
+ c->timedout = 1;
+ ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT);
+ return;
+ }
+
+ if (upstream->read->timedout || upstream->write->timedout) {
+ ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (from_upstream) {
+ src = upstream;
+ dst = downstream;
+ b = &u->buffer;
+
+ } else {
+ src = downstream;
+ dst = upstream;
+ b = &u->from_client;
+
+ if (r->header_in->last > r->header_in->pos) {
+ b = r->header_in;
+ b->end = b->last;
+ do_write = 1;
+ }
+
+ if (b->start == NULL) {
+ b->start = ngx_palloc(r->pool, u->conf->buffer_size);
+ if (b->start == NULL) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ b->pos = b->start;
+ b->last = b->start;
+ b->end = b->start + u->conf->buffer_size;
+ b->temporary = 1;
+ b->tag = u->output.tag;
+ }
+ }
+
+ for ( ;; ) {
+
+ if (do_write) {
+
+ size = b->last - b->pos;
+
+ if (size && dst->write->ready) {
+
+ n = dst->send(dst, b->pos, size);
+
+ if (n == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (n > 0) {
+ b->pos += n;
+
+ if (b->pos == b->last) {
+ b->pos = b->start;
+ b->last = b->start;
+ }
+ }
+ }
+ }
+
+ size = b->end - b->last;
+
+ if (size && src->read->ready) {
+
+ n = src->recv(src, b->last, size);
+
+ if (n == NGX_AGAIN || n == 0) {
+ break;
+ }
+
+ if (n > 0) {
+ do_write = 1;
+ b->last += n;
+
+ continue;
+ }
+
+ if (n == NGX_ERROR) {
+ src->read->eof = 1;
+ }
+ }
+
+ break;
+ }
+
+ if ((upstream->read->eof && u->buffer.pos == u->buffer.last)
+ || (downstream->read->eof && u->from_client.pos == u->from_client.last)
+ || (downstream->read->eof && upstream->read->eof))
+ {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream upgraded done");
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (ngx_handle_write_event(upstream->write, u->conf->send_lowat)
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (upstream->write->active && !upstream->write->ready) {
+ ngx_add_timer(upstream->write, u->conf->send_timeout);
+
+ } else if (upstream->write->timer_set) {
+ ngx_del_timer(upstream->write);
+ }
+
+ if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (upstream->read->active && !upstream->read->ready) {
+ ngx_add_timer(upstream->read, u->conf->read_timeout);
+
+ } else if (upstream->read->timer_set) {
+ ngx_del_timer(upstream->read);
+ }
+
+ if (ngx_handle_write_event(downstream->write, clcf->send_lowat)
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (ngx_handle_read_event(downstream->read, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (downstream->write->active && !downstream->write->ready) {
+ ngx_add_timer(downstream->write, clcf->send_timeout);
+
+ } else if (downstream->write->timer_set) {
+ ngx_del_timer(downstream->write);
+ }
+}
+
+
+static void
ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r)
{
ngx_event_t *wev;
@@ -2968,19 +3276,10 @@ ngx_http_upstream_cleanup(void *data)
{
ngx_http_request_t *r = data;
- ngx_http_upstream_t *u;
-
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"cleanup http upstream request: \"%V\"", &r->uri);
- u = r->upstream;
-
- if (u->resolved && u->resolved->ctx) {
- ngx_resolve_name_done(u->resolved->ctx);
- u->resolved->ctx = NULL;
- }
-
- ngx_http_upstream_finalize_request(r, u, NGX_DONE);
+ ngx_http_upstream_finalize_request(r, r->upstream, NGX_DONE);
}
@@ -3008,7 +3307,7 @@ ngx_http_upstream_finalize_request(ngx_http_request_t *r,
u->state->response_sec = tp->sec - u->state->response_sec;
u->state->response_msec = tp->msec - u->state->response_msec;
- if (u->pipe) {
+ if (u->pipe && u->pipe->read_length) {
u->state->response_length = u->pipe->read_length;
}
}
@@ -4130,6 +4429,7 @@ ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
value = cf->args->elts;
u.host = value[1];
u.no_resolve = 1;
+ u.no_port = 1;
uscf = ngx_http_upstream_add(cf, &u, NGX_HTTP_UPSTREAM_CREATE
|NGX_HTTP_UPSTREAM_WEIGHT
@@ -4400,14 +4700,14 @@ ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)
return NULL;
}
- if ((uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE) && u->port) {
+ if ((uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE) && !u->no_port) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"upstream \"%V\" may not have port %d",
&u->host, u->port);
return NULL;
}
- if ((flags & NGX_HTTP_UPSTREAM_CREATE) && uscfp[i]->port) {
+ if ((flags & NGX_HTTP_UPSTREAM_CREATE) && !uscfp[i]->no_port) {
ngx_log_error(NGX_LOG_WARN, cf->log, 0,
"upstream \"%V\" may not have port %d in %s:%ui",
&u->host, uscfp[i]->port,
@@ -4415,7 +4715,9 @@ ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)
return NULL;
}
- if (uscfp[i]->port != u->port) {
+ if (uscfp[i]->port && u->port
+ && uscfp[i]->port != u->port)
+ {
continue;
}
@@ -4443,6 +4745,7 @@ ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)
uscf->line = cf->conf_file->line;
uscf->port = u->port;
uscf->default_port = u->default_port;
+ uscf->no_port = u->no_port;
if (u->naddrs == 1) {
uscf->servers = ngx_array_create(cf->pool, 1,
@@ -4479,24 +4782,63 @@ ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
{
char *p = conf;
- ngx_int_t rc;
- ngx_str_t *value;
- ngx_addr_t **paddr;
+ ngx_int_t rc;
+ ngx_str_t *value;
+ ngx_http_complex_value_t cv;
+ ngx_http_upstream_local_t **plocal, *local;
+ ngx_http_compile_complex_value_t ccv;
- paddr = (ngx_addr_t **) (p + cmd->offset);
+ plocal = (ngx_http_upstream_local_t **) (p + cmd->offset);
- *paddr = ngx_palloc(cf->pool, sizeof(ngx_addr_t));
- if (*paddr == NULL) {
- return NGX_CONF_ERROR;
+ if (*plocal != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
}
value = cf->args->elts;
- rc = ngx_parse_addr(cf->pool, *paddr, value[1].data, value[1].len);
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ *plocal = NULL;
+ return NGX_CONF_OK;
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &cv;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ local = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_local_t));
+ if (local == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *plocal = local;
+
+ if (cv.lengths) {
+ local->value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+ if (local->value == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *local->value = cv;
+
+ return NGX_CONF_OK;
+ }
+
+ local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t));
+ if (local->addr == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ rc = ngx_parse_addr(cf->pool, local->addr, value[1].data, value[1].len);
switch (rc) {
case NGX_OK:
- (*paddr)->name = value[1];
+ local->addr->name = value[1];
return NGX_CONF_OK;
case NGX_DECLINED:
@@ -4510,6 +4852,53 @@ ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
}
+static ngx_addr_t *
+ngx_http_upstream_get_local(ngx_http_request_t *r,
+ ngx_http_upstream_local_t *local)
+{
+ ngx_int_t rc;
+ ngx_str_t val;
+ ngx_addr_t *addr;
+
+ if (local == NULL) {
+ return NULL;
+ }
+
+ if (local->value == NULL) {
+ return local->addr;
+ }
+
+ if (ngx_http_complex_value(r, local->value, &val) != NGX_OK) {
+ return NULL;
+ }
+
+ if (val.len == 0) {
+ return NULL;
+ }
+
+ addr = ngx_palloc(r->pool, sizeof(ngx_addr_t));
+ if (addr == NULL) {
+ return NULL;
+ }
+
+ rc = ngx_parse_addr(r->pool, addr, val.data, val.len);
+
+ switch (rc) {
+ case NGX_OK:
+ addr->name = val;
+ return addr;
+
+ case NGX_DECLINED:
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "invalid local address \"%V\"", &val);
+ /* fall through */
+
+ default:
+ return NULL;
+ }
+}
+
+
char *
ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)