/http_copy/ngx_http_copy_module.c
C | 1813 lines | 1265 code | 426 blank | 122 comment | 306 complexity | 6f0a641234d91e335f3680c3a3eadac2 MD5 | raw file
- /*
- * Copyright (C) Xiaochen Wang (xiaochen.wxc@alibaba-inc.com)
- * Copyright (C) Taobao, Inc.
- * Copyright (C) Alibaba, Inc.
- */
- #include <ngx_config.h>
- #include <ngx_core.h>
- #include <ngx_http.h>
- typedef struct {
- ngx_addr_t *addrs;
- ngx_uint_t naddrs;
- ngx_uint_t multiple;
- ngx_queue_t cache_connections; /* cached connection list*/
- ngx_int_t cached;
- ngx_int_t max_cached;
- ngx_msec_t cached_timeout;
- ngx_int_t max_connection;
- ngx_int_t connection;
- ngx_flag_t on;
- ngx_flag_t keepalive;
- ngx_flag_t force_keepalive;
- ngx_flag_t serial;
- } ngx_http_copy_loc_conf_t;
- typedef struct {
- ngx_uint_t state;
- off_t size;
- } ngx_http_copy_chunk_t;
- typedef struct ngx_http_copy_request_s ngx_http_copy_request_t;
- struct ngx_http_copy_request_s {
- ngx_http_request_t *r; /* incoming request */
- ngx_pool_t *pool;
- ngx_peer_connection_t peer;
- ngx_chain_t *request_bufs;
- ngx_buf_t *buffer;
- ngx_output_chain_ctx_t output;
- ngx_chain_writer_ctx_t writer;
- ngx_http_request_t response; /* used by http response parser */
- ngx_http_status_t status; /* used by http response parser */
- off_t length; /* response body length or chunk body size */
- ngx_http_copy_chunk_t *chunk;
- ngx_http_copy_loc_conf_t *cplcf; /* used when response is sent back */
- ngx_int_t (*process_header)(ngx_http_copy_request_t *cpr);
- ngx_queue_t queue; /* in ngx_http_copy_ctx_t::copy_request */
- /* serial copy */
- ngx_chain_t *serial_request_bufs;
- ngx_uint_t serial_sent;
- unsigned discard_body:2;
- unsigned request_sent:1;
- unsigned keepalive_connect:1;
- unsigned connect:1;
- unsigned serial:1;
- };
- typedef struct {
- ngx_queue_t copy_request;
- } ngx_http_copy_ctx_t;
- typedef struct {
- /* long time */
- ngx_atomic_t request_count;
- ngx_atomic_t response_count;
- ngx_atomic_t response_ok_count;
- ngx_atomic_t response_err_count;
- ngx_atomic_t connect_count;
- ngx_atomic_t connect_keepalive_count;
- ngx_atomic_t read_bytes;
- ngx_atomic_t read_chunk_bytes;
- ngx_atomic_t write_bytes;
- /* real time */
- ngx_atomic_t active_connect;
- ngx_atomic_t active_connect_keepalive;
- } ngx_http_copy_status_shm_t;
- static ngx_int_t ngx_http_copy_test_connect(ngx_connection_t *c);
- static ngx_int_t ngx_http_copy_init(ngx_conf_t *conf);
- static void *ngx_http_copy_create_loc_conf(ngx_conf_t *conf);
- static ngx_int_t ngx_http_copy_handler(ngx_http_request_t *r);
- static char *ngx_http_copy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
- static ngx_int_t ngx_http_copy_send_request(ngx_http_copy_request_t *cpr);
- static char *ngx_http_copy_status(ngx_conf_t *cf, ngx_command_t *cmd,
- void *conf);
- static void ngx_http_copy_dummy_handler(ngx_event_t *ev);
- static ngx_int_t ngx_http_copy_try_keepalive_connection(ngx_http_copy_request_t *cpr);
- static char * ngx_http_copy_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,
- void *conf);
- static char * ngx_http_copy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);
- static char * ngx_http_copy_init_shm(ngx_conf_t *cf);
- static ngx_int_t ngx_chain_buf_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,
- ngx_chain_t *in);
- static ngx_int_t ngx_http_copy_parse_status_line(ngx_http_copy_request_t *cpr);
- #define NGX_HTTP_COPY_DISCARD_BODY 0x01 /* 0b01 */
- #define NGX_HTTP_COPY_DISCARD_CHUNK_BODY 0x02 /* 0b10 */
- /* define directive in nginx.conf */
- static ngx_command_t ngx_http_copy_commands[] = {
- { ngx_string("http_copy"),
- NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
- ngx_http_copy,
- NGX_HTTP_LOC_CONF_OFFSET,
- 0,
- NULL },
- { ngx_string("http_copy_keepalive"),
- NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
- ngx_http_copy_keepalive,
- NGX_HTTP_LOC_CONF_OFFSET,
- 0,
- NULL },
- { ngx_string("http_copy_status"),
- NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
- ngx_http_copy_status,
- 0,
- 0,
- NULL },
- ngx_null_command
- };
- static ngx_http_module_t ngx_http_copy_module_ctx = {
- NULL, /* preconfiguration */
- ngx_http_copy_init, /* postconfiguration */
- NULL, /* create main configuration */
- NULL, /* init main configuration */
- NULL, /* create server configuration */
- NULL, /* merge server configuration */
- ngx_http_copy_create_loc_conf, /* create location configuration */
- ngx_http_copy_merge_loc_conf /* merge location configuration */
- };
- ngx_module_t ngx_http_copy_module = {
- NGX_MODULE_V1,
- &ngx_http_copy_module_ctx,
- ngx_http_copy_commands,
- NGX_HTTP_MODULE,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NGX_MODULE_V1_PADDING
- };
- static ngx_http_input_body_filter_pt ngx_http_next_input_body_filter;
- static ngx_http_copy_status_shm_t *copy_status = NULL;
- static char ngx_http_copy_version[] = " HTTP/1.0" CRLF;
- static char ngx_http_copy_version_11[] = " HTTP/1.1" CRLF;
- static ngx_connection_t *
- ngx_http_copy_get_keepalive_connection(ngx_http_copy_request_t *cpr)
- {
- ngx_http_copy_loc_conf_t *cplcf = cpr->cplcf;
- ngx_queue_t *q;
- ngx_connection_t *c;
- if (cplcf->keepalive && ngx_queue_empty(&cplcf->cache_connections) == 0) {
- /* get connection from connection cache */
- q = ngx_queue_last(&cplcf->cache_connections);
- c = ngx_queue_data(q, ngx_connection_t, queue);
- ngx_queue_remove(&c->queue);
- cplcf->cached--;
- ngx_log_error(NGX_LOG_INFO, cpr->peer.log, 0,
- "[copy] keepalive: get no.%d cached connection %p",
- cplcf->cached + 1, c);
- /* reinit connection, although ngx_http_copy_connect will init it also */
- c->idle = 0;
- c->data = cpr;
- c->read->handler = NULL; /* use dummy_handler()? */
- c->write->handler = NULL;
- c->log = cpr->peer.log;
- c->read->log = cpr->peer.log;
- c->write->log = cpr->peer.log;
- c->pool = cpr->pool;
- if (c->read->timer_set) {
- ngx_del_timer(c->read);
- }
- /* assert write timer is deleted */
- if (c->write->timer_set) {
- ngx_del_timer(c->write);
- }
- (void) ngx_atomic_fetch_add(©_status->connect_keepalive_count, 1);
- cpr->keepalive_connect = 1;
- /* not necessary to detect whether cached connection is valid */
- return c;
- }
- return NULL;
- }
- static void
- ngx_http_copy_keepalive_close_handler(ngx_event_t *ev)
- {
- ngx_connection_t *c = ev->data;
- ngx_http_copy_loc_conf_t *cplcf;
- ngx_int_t n;
- char buf[1];
- /* keepalive timedout */
- if (ev->timedout) {
- ngx_log_error(NGX_LOG_INFO, ev->log, 0,
- "[copy] keepalive: cached connection is timed out");
- goto close;
- }
- /*
- * ngx_worker_process_cycle() will set it when receiving EXITING signal.
- * and then it calls c->read->handler(ngx_http_copy_keepalive_close_handler)
- *
- * Note although ngx_drain_connections() could set it, but this connection
- * has been deleted from ngx_cycle->reusable_connections_queue.
- * So ngx_drain_connections() cannot touch this conneciton.
- */
- if (c->close) {
- ngx_log_error(NGX_LOG_INFO, ev->log, 0,
- "[copy] keepalive: server is exiting");
- goto close;
- }
- /* detect whether connection is closed by peer */
- n = recv(c->fd, buf, 1, MSG_PEEK);
- if (n == -1 && ngx_socket_errno == NGX_EAGAIN) {
- if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
- goto close;
- }
- return;
- }
- /* TCP RESET or TCP HALF CLOSE */
- ngx_log_error(NGX_LOG_INFO, ev->log, 0,
- "[copy] keepalive: the peer has closed connection");
- close:
- // TODO: debug cplcf
- cplcf = c->data;
- ngx_log_error(NGX_LOG_INFO, ev->log, 0,
- "[copy] keepalive: close no.%d cached connection %p",
- cplcf->cached, c);
- /* delete it from connection cache */
- ngx_queue_remove(&c->queue);
- cplcf->cached--;
- c->pool = NULL; /* pool in cpr has been destroyed */
- ngx_close_connection(c);
- cplcf->connection--;
- }
- static ngx_int_t
- ngx_http_copy_try_keepalive_connection(ngx_http_copy_request_t *cpr)
- {
- ngx_connection_t *c = cpr->peer.connection;
- ngx_http_copy_loc_conf_t *cplcf = cpr->cplcf;
- if (c == NULL || !cplcf->keepalive || !cpr->response.keepalive) {
- return 0;
- }
- if (cplcf->cached >= cplcf->max_cached) {
- ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
- "[copy] keepalive: keepalive connection cache is full");
- return 0;
- }
- if (c->read->eof || c->read->error || c->read->timedout
- || c->write->error || c->write->timedout)
- {
- return 0;
- }
- if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
- return 0;
- }
- /* cache valid connections */
- ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
- "[copy] keepalive: save no.%d connection %p",
- cplcf->cached + 1, c);
- cpr->peer.connection = NULL; /* skip ngx_close_connection() */
- /* add to cache: hack c->queue */
- /* delete it from reusable connection list _if necessary_ */
- ngx_reusable_connection(c, 0);
- /* add it to connection cache */
- ngx_queue_insert_head(&cplcf->cache_connections, &c->queue);
- cplcf->cached++;
- if (c->write->timer_set) {
- ngx_del_timer(c->write);
- }
- ngx_add_timer(c->read, cplcf->cached_timeout);
- c->write->handler = ngx_http_copy_dummy_handler;
- c->read->handler = ngx_http_copy_keepalive_close_handler;
- c->data = cplcf; /* maybe modify cplcf->connection */
- c->idle = 1;
- c->log = ngx_cycle->log;
- c->read->log = c->log;
- c->write->log = c->log;
- /* c->pool is NULL */
- if (c->read->ready) {
- ngx_http_copy_keepalive_close_handler(c->read);
- }
- return 1;
- }
- static ngx_int_t
- ngx_http_copy_get_peer(ngx_peer_connection_t *pc, void *data)
- {
- ngx_http_copy_request_t *cpr = data;
- ngx_http_copy_loc_conf_t *cplcf = cpr->cplcf;
- pc->sockaddr = cplcf->addrs[0].sockaddr;
- pc->socklen = cplcf->addrs[0].socklen;
- pc->name = &cplcf->addrs[0].name;
- pc->connection = ngx_http_copy_get_keepalive_connection(cpr);
- /* no keepalive connection & connections limit */
- if (cplcf->connection > cplcf->max_connection && pc->connection == NULL) {
- ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
- "[copy] open too many connections");
- return NGX_BUSY;
- }
- return pc->connection ? NGX_DONE : NGX_OK;
- }
- static void
- ngx_http_copy_finalize_request(ngx_http_copy_request_t *cpr)
- {
- if (cpr->connect) {
- if (cpr->keepalive_connect) {
- (void) ngx_atomic_fetch_add(©_status->active_connect_keepalive,
- -1);
- }
- (void) ngx_atomic_fetch_add(©_status->active_connect, -1);
- }
- /* wont receive body in input body filter */
- if (cpr->r) {
- ngx_log_error(NGX_LOG_DEBUG, cpr->r->connection->log, 0,
- "[copy] cpr %p disattach request \"%V\"",
- cpr, &cpr->r->uri);
- ngx_queue_remove(&cpr->queue);
- }
- /* close connection(not cached) */
- if (cpr->peer.connection) {
- cpr->peer.connection->pool = NULL; /* equal to cpr->pool */
- ngx_close_connection(cpr->peer.connection);
- cpr->cplcf->connection--;
- cpr->peer.connection = NULL;
- }
- /* free copy request */
- ngx_destroy_pool(cpr->pool);
- }
- static ngx_inline void
- ngx_http_copy_next(ngx_http_copy_request_t *cpr)
- {
- /* serial send request */
- if (cpr->serial) {
- ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
- "[copy] serial: no.%d request has been sent",
- cpr->serial_sent + 1);
- if (++cpr->serial_sent >= cpr->cplcf->multiple) {
- ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
- "[copy] serial: total requests have been sent");
- goto next;
- }
- if (!cpr->response.keepalive) {
- ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
- "[copy] serial: keepalive is disabled, cant send no.%d request",
- cpr->serial_sent);
- goto next;
- }
- cpr->request_sent = 0;
- cpr->request_bufs = NULL;
- ngx_chain_buf_add_copy(cpr->pool, &cpr->request_bufs, cpr->serial_request_bufs);
- cpr->discard_body = 0;
- cpr->length = -1;
- cpr->process_header = ngx_http_copy_parse_status_line;
- ngx_memzero(&cpr->response, sizeof(ngx_http_request_t));
- ngx_memzero(&cpr->status, sizeof(ngx_http_status_t));
- if (cpr->buffer != NULL) {
- cpr->buffer->pos = cpr->buffer->start;
- cpr->buffer->last = cpr->buffer->start;
- }
- /* wevent has been deleted, revent will be readded by ngx_http_copy_send_request() */
- ngx_http_copy_send_request(cpr);
- return;
- }
- next:
- (void) ngx_http_copy_try_keepalive_connection(cpr);
- ngx_http_copy_finalize_request(cpr);
- }
- static ngx_int_t
- ngx_http_copy_test_connect(ngx_connection_t *c)
- {
- int err;
- socklen_t len;
- if (c->log->action == NULL) {
- c->log->action = "connecting to backend server";
- }
- #if (NGX_HAVE_KQUEUE)
- if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
- if (c->write->pending_eof || c->read->pending_eof) {
- if (c->write->pending_eof) {
- err = c->write->kq_errno;
- } else {
- err = c->read->kq_errno;
- }
- /* ngx_cycle->log->handler dont print 'while %s' */
- ngx_log_error(NGX_LOG_ERR, c->log, err,
- "[copy] kevent() reported that connect() failed while %s",
- c->log->action);
- return NGX_ERROR;
- }
- }/* else: drop else, let it call getsockopt after kqueue test */
- #endif
- {
- err = 0;
- len = sizeof(int);
- /*
- * BSDs and Linux return 0 and set a pending error in err
- * Solaris returns -1 and sets errno
- */
- if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)
- == -1)
- {
- err = ngx_errno;
- }
- if (err) {
- ngx_log_error(NGX_LOG_ERR, c->log, err,
- "[copy] getsockopt() reported that connect() failed while %s",
- c->log->action);
- return NGX_ERROR;
- }
- }
- return NGX_OK;
- }
- static void
- ngx_http_copy_dummy_handler(ngx_event_t *ev)
- {
- /*
- * When added, wev is triggered at once.
- * So you should use DEBUG log_level to avoid noice.
- */
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, "[copy] dummy handler");
- }
- static void
- ngx_http_copy_send_request_handler(ngx_event_t *ev)
- {
- ngx_connection_t *c;
- ngx_http_copy_request_t *cpr;
- c = ev->data;
- cpr = c->data;
- if (c->write->timedout) {
- ngx_http_copy_finalize_request(cpr);
- return;
- }
- (void) ngx_http_copy_send_request(cpr);
- }
- static void
- ngx_http_copy_connected_handler(ngx_event_t *ev)
- {
- ngx_connection_t *c = ev->data;
- if (c->write->timedout == 0) {
- ngx_log_error(NGX_LOG_DEBUG, c->log, 0,
- "[copy] nonblocking connection is established");
- (void) ngx_atomic_fetch_add(©_status->connect_count, 1);
- }
- ngx_http_copy_send_request_handler(ev);
- }
- static void
- ngx_http_copy_discard_body(ngx_http_copy_request_t *cpr)
- {
- u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];
- ngx_connection_t *c = cpr->peer.connection;
- ssize_t n;
- size_t size;
- /* recv and discard response body */
- for ( ;; ) {
- /*
- * nginx upstream doesnt do it, which maybe readahead something wrong
- * that client sent
- */
- if (cpr->length == 0) {
- break;
- }
- size = (cpr->length != -1 && cpr->length < NGX_HTTP_DISCARD_BUFFER_SIZE)
- ? cpr->length
- : NGX_HTTP_DISCARD_BUFFER_SIZE;
- n = c->recv(c, buffer, size);
- if (n == NGX_AGAIN) {
- break;
- } else if (n == NGX_ERROR) {
- ngx_http_copy_finalize_request(cpr);
- return;
- } else if (n == 0) {
- /* backend server closes connection. */
- ngx_http_copy_finalize_request(cpr);
- return;
- }
- /* n > 0 */
- if (cpr->length != -1) {
- cpr->length -= n;
- }
- (void) ngx_atomic_fetch_add(©_status->read_bytes, n);
- }
- /* n == NGX_AGAIN */
- if (cpr->length == 0) {
- ngx_http_copy_next(cpr); /* try keepalive */
- return;
- }
- // TODO: re-add timer
- if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
- ngx_http_copy_finalize_request(cpr);
- return;
- }
- }
- static ngx_int_t
- ngx_http_copy_process_one_header(ngx_http_copy_request_t *cpr)
- {
- ngx_http_request_t *r = &cpr->response;
- ngx_keyval_t h;
- /* assert one header has been parsed */
- h.key.len = r->header_name_end - r->header_name_start;
- h.key.data = r->header_name_start;
- h.value.len = r->header_end - r->header_start;
- h.value.data = r->header_start;
- #define header_key_is(s) \
- (h.key.len == sizeof(s) - 1 \
- && ngx_strncmp(h.key.data, (s), sizeof(s) - 1) == 0)
- #define header_value_has(s) \
- (ngx_strlcasestrn(h.value.data, h.value.data + h.value.len, \
- (u_char *)(s), sizeof(s) - 1 - 1/* len - 1 */) \
- != NULL)
- if (header_key_is("Content-Length")) {
- r->headers_in.content_length_n = ngx_atoof(h.value.data, h.value.len);
- cpr->length = r->headers_in.content_length_n;
- } else if (header_key_is("Connection")) {
- if (header_value_has("close")) {
- r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
- }
- } else if (header_key_is("Transfer-Encoding")) {
- if (header_value_has("chunked")) {
- r->chunked = 1;
- }
- }
- return NGX_OK;
- }
- static ngx_int_t
- ngx_http_copy_parse_header(ngx_http_copy_request_t *cpr)
- {
- ngx_http_request_t *r = &cpr->response;
- ngx_int_t rc;
- /* parse header */
- for ( ;; ) {
- rc = ngx_http_parse_header_line(&cpr->response, cpr->buffer, 1);
- if (rc == NGX_OK) {
- /* process this header */
- (void) ngx_http_copy_process_one_header(cpr);
- continue;
- }
- if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
- /* process response args */
- if (r->chunked) {
- r->headers_in.content_length_n = -1;
- cpr->length = -1;
- }
- if (r->headers_out.status == NGX_HTTP_NO_CONTENT
- || r->headers_out.status == NGX_HTTP_NOT_MODIFIED)
- {
- cpr->length = 0;
- }
- r->keepalive = (r->headers_in.connection_type != NGX_HTTP_CONNECTION_CLOSE);
- return NGX_OK;
- }
- /* rc == NGX_ERROR || rc == NGX_AGAIN */
- return rc;
- }
- }
- static ngx_int_t
- ngx_http_copy_parse_status_line(ngx_http_copy_request_t *cpr)
- {
- ngx_int_t rc;
- /* process status line */
- rc = ngx_http_parse_status_line(&cpr->response, cpr->buffer, &cpr->status);
- if (rc == NGX_AGAIN) {
- return rc;
- }
- if (rc == NGX_ERROR) {
- ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
- "[copy] HTTP response: invalid status line");
- return rc;
- }
- /* rc == NGX_OK */
- cpr->response.headers_out.status = cpr->status.code;
- cpr->response.headers_out.status_line.len = cpr->status.end - cpr->status.start;
- cpr->response.headers_out.status_line.data = cpr->status.start; /* not necessary for new buffer */
- if (cpr->status.http_version < NGX_HTTP_VERSION_11) {
- cpr->response.headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
- }
- cpr->process_header = ngx_http_copy_parse_header;
- return ngx_http_copy_parse_header(cpr);
- }
- static ngx_int_t
- ngx_http_copy_parse_chunked(ngx_http_copy_chunk_t *ctx, ngx_buf_t *buf)
- {
- u_char *pos, ch, c;
- ngx_int_t rc;
- enum {
- sw_chunk_start = 0,
- sw_chunk_size,
- sw_chunk_extension,
- sw_chunk_extension_almost_done,
- sw_chunk_data,
- sw_after_data,
- sw_after_data_almost_done,
- sw_last_chunk_extension,
- sw_last_chunk_extension_almost_done,
- sw_trailer,
- sw_trailer_almost_done,
- sw_trailer_header,
- sw_trailer_header_almost_done
- } state;
- state = ctx->state;
- if (state == sw_chunk_data && ctx->size == 0) {
- state = sw_after_data;
- }
- rc = NGX_AGAIN;
- for (pos = buf->pos; pos < buf->last; pos++) {
- ch = *pos;
- switch (state) {
- case sw_chunk_start:
- if (ch >= '0' && ch <= '9') {
- state = sw_chunk_size;
- ctx->size = ch - '0';
- break;
- }
- c = (u_char) (ch | 0x20);
- if (c >= 'a' && c <= 'f') {
- state = sw_chunk_size;
- ctx->size = c - 'a' + 10;
- break;
- }
- goto invalid;
- case sw_chunk_size:
- if (ch >= '0' && ch <= '9') {
- ctx->size = ctx->size * 16 + (ch - '0');
- break;
- }
- c = (u_char) (ch | 0x20);
- if (c >= 'a' && c <= 'f') {
- ctx->size = ctx->size * 16 + (c - 'a' + 10);
- break;
- }
- if (ctx->size == 0) {
- switch (ch) {
- case CR:
- state = sw_last_chunk_extension_almost_done;
- break;
- case LF:
- state = sw_trailer;
- break;
- case ';':
- case ' ':
- case '\t':
- state = sw_last_chunk_extension;
- break;
- default:
- goto invalid;
- }
- break;
- }
- switch (ch) {
- case CR:
- state = sw_chunk_extension_almost_done;
- break;
- case LF:
- state = sw_chunk_data;
- break;
- case ';':
- case ' ':
- case '\t':
- state = sw_chunk_extension;
- break;
- default:
- goto invalid;
- }
- break;
- case sw_chunk_extension:
- switch (ch) {
- case CR:
- state = sw_chunk_extension_almost_done;
- break;
- case LF:
- state = sw_chunk_data;
- }
- break;
- case sw_chunk_extension_almost_done:
- if (ch == LF) {
- state = sw_chunk_data;
- break;
- }
- goto invalid;
- case sw_chunk_data:
- rc = NGX_OK;
- goto data;
- case sw_after_data:
- switch (ch) {
- case CR:
- state = sw_after_data_almost_done;
- break;
- case LF:
- state = sw_chunk_start;
- }
- break;
- case sw_after_data_almost_done:
- if (ch == LF) {
- state = sw_chunk_start;
- break;
- }
- goto invalid;
- case sw_last_chunk_extension:
- switch (ch) {
- case CR:
- state = sw_last_chunk_extension_almost_done;
- break;
- case LF:
- state = sw_trailer;
- }
- break;
- case sw_last_chunk_extension_almost_done:
- if (ch == LF) {
- state = sw_trailer;
- break;
- }
- goto invalid;
- case sw_trailer:
- switch (ch) {
- case CR:
- state = sw_trailer_almost_done;
- break;
- case LF:
- goto done;
- default:
- state = sw_trailer_header;
- }
- break;
- case sw_trailer_almost_done:
- if (ch == LF) {
- goto done;
- }
- goto invalid;
- case sw_trailer_header:
- switch (ch) {
- case CR:
- state = sw_trailer_header_almost_done;
- break;
- case LF:
- state = sw_trailer;
- }
- break;
- case sw_trailer_header_almost_done:
- if (ch == LF) {
- state = sw_trailer;
- break;
- }
- goto invalid;
- }
- }
- data:
- ctx->state = state;
- buf->pos = pos;
- return rc;
- done:
- return NGX_DONE;
- invalid:
- return NGX_ERROR;
- }
- static void
- ngx_http_copy_discard_chunk_body(ngx_http_copy_request_t *cpr)
- {
- ngx_connection_t *c = cpr->peer.connection;
- ngx_buf_t *buf;
- ngx_http_copy_chunk_t *chunk;
- ngx_int_t rc;
- ssize_t n;
- buf = cpr->buffer;
- chunk = cpr->chunk;
- for ( ;; ) {
- /* read response */
- if (buf->pos >= buf->last) {
- buf->pos = buf->start;
- buf->last = buf->start;
- n = c->recv(c, buf->pos, buf->end - buf->start);
- if (n == NGX_AGAIN) {
- // TODO: re-add timer
- if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
- ngx_http_copy_finalize_request(cpr);
- return;
- }
- return;
- } else if (n == NGX_ERROR) {
- ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
- "[copy] discard chunk body: read error occured");
- ngx_http_copy_finalize_request(cpr);
- return;
- } else if (n == 0) {
- /* backend server closes connection. */
- ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
- "[copy] discard chunk body: backend closed connection");
- ngx_http_copy_finalize_request(cpr);
- return;
- }
- /* n > 0 */
- buf->last = buf->pos + n;
- }
- /* discard chunk */
- if (cpr->length > 0) {
- if (cpr->length > buf->last - buf->pos) {
- cpr->length -= buf->last - buf->pos;
- buf->pos = buf->last;
- } else {
- buf->pos += cpr->length; /* maybe buf->pos == buf->last */
- cpr->length = 0;
- }
- if (cpr->length > 0 || buf->pos == buf->last) {
- continue;
- }
- }
- /* parse chunk */
- rc = ngx_http_copy_parse_chunked(cpr->chunk, buf);
- if (rc == NGX_AGAIN) {
- /* continue to read more data */
- continue;
- }
- if (rc == NGX_OK) {
- /* chunk is parsed, continue to discard chunk body */
- (void) ngx_atomic_fetch_add(©_status->read_bytes, chunk->size);
- (void) ngx_atomic_fetch_add(©_status->read_chunk_bytes, chunk->size);
- cpr->length = chunk->size; /* length: size of current chunk */
- chunk->size = 0; /* chunk->state will goto sw_after_data */
- continue;
- }
- if (rc == NGX_DONE) {
- /* a whole response is pared */
- ngx_http_copy_next(cpr); /* try keepalive */
- return;
- }
- /* rc == NGX_ERROR, invalid response */
- ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
- "[copy] discard chunk body: backend sent invalid chunked response");
- ngx_http_copy_finalize_request(cpr);
- return;
- }
- }
- static void
- ngx_http_copy_discard_response(ngx_http_copy_request_t *cpr)
- {
- ngx_int_t n, rc;
- ngx_connection_t *c = cpr->peer.connection;
- c->log->action = "discarding response";
- if (!cpr->request_sent && ngx_http_copy_test_connect(c) != NGX_OK) {
- ngx_http_copy_finalize_request(cpr);
- return;
- }
- if (cpr->discard_body == NGX_HTTP_COPY_DISCARD_BODY) {
- ngx_http_copy_discard_body(cpr);
- return;
- } else if (cpr->discard_body == NGX_HTTP_COPY_DISCARD_CHUNK_BODY) {
- ngx_http_copy_discard_chunk_body(cpr);
- return;
- }
- /* create buffer for response, detect for reenterring */
- if (cpr->buffer == NULL) {
- cpr->buffer = ngx_create_temp_buf(cpr->pool, 4096);
- if (cpr->buffer == NULL) {
- ngx_http_copy_finalize_request(cpr);
- return;
- }
- }
- /* recv and parse response header */
- for ( ;; ) {
- /* read response */
- n = c->recv(c, cpr->buffer->last, cpr->buffer->end - cpr->buffer->last);
- if (n == NGX_AGAIN) {
- // TODO: re-add timer
- if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
- ngx_http_copy_finalize_request(cpr);
- return;
- }
- return;
- } else if (n == NGX_ERROR) {
- /* if worker_connections is too small, it maybe come here */
- ngx_log_error(NGX_LOG_ERR, c->log, 0,
- "[copy] discard body: read error occured");
- /* TODO: status log */
- ngx_http_copy_finalize_request(cpr);
- return;
- } else if (n == 0) {
- /* backend server closes connection. */
- ngx_log_error(NGX_LOG_ERR, c->log, 0,
- "[copy] discard body: backend closed connection");
- /* TODO: status log */
- ngx_http_copy_finalize_request(cpr);
- return;
- }
- /* n > 0: parse response */
- cpr->buffer->last += n;
- rc = cpr->process_header(cpr);
- if (rc == NGX_ERROR) {
- ngx_http_copy_finalize_request(cpr);
- return;
- } else if (rc == NGX_OK) {
- /* log status info */
- (void) ngx_atomic_fetch_add(©_status->response_count, 1);
- if (cpr->status.code >= NGX_HTTP_OK
- && cpr->status.code < NGX_HTTP_BAD_REQUEST)
- {
- (void) ngx_atomic_fetch_add(©_status->response_ok_count, 1);
- } else {
- (void) ngx_atomic_fetch_add(©_status->response_err_count, 1);
- }
- /* chunked response */
- if (cpr->response.chunked) {
- cpr->chunk = ngx_pcalloc(cpr->pool, sizeof(ngx_http_copy_chunk_t));
- if (cpr->chunk == NULL) {
- ngx_http_copy_finalize_request(cpr);
- return;
- }
- cpr->discard_body = NGX_HTTP_COPY_DISCARD_CHUNK_BODY;
- ngx_http_copy_discard_chunk_body(cpr);
- return;
- }
- /* discard data left in buffer */
- n = cpr->buffer->last - cpr->buffer->pos;
- if (n > 0) {
- (void) ngx_atomic_fetch_add(©_status->read_bytes, n);
- cpr->buffer->pos = cpr->buffer->last;
- if (cpr->length > 0) {
- cpr->length -= n;
- }
- }
- /* discard response body(headers + body) */
- cpr->discard_body = NGX_HTTP_COPY_DISCARD_BODY;
- ngx_http_copy_discard_body(cpr);
- return;
- }
- /* rc == NGX_AGAIN: read again and parse */
- if (cpr->buffer->last == cpr->buffer->end) {
- ngx_log_error(NGX_LOG_ERR, c->log, 0,
- "[copy] response header is too big ( > 4096 bytes)");
- ngx_http_copy_finalize_request(cpr);
- return;
- }
- }
- }
- static void
- ngx_http_copy_recv_response_handler(ngx_event_t *ev)
- {
- ngx_connection_t *c;
- ngx_http_copy_request_t *cpr;
- c = ev->data;
- cpr = c->data;
- if (c->read->timedout) {
- if (cpr->discard_body == NGX_HTTP_COPY_DISCARD_BODY) {
- ngx_log_error(NGX_LOG_ERR, c->log, 0,
- "[copy] discard body: read timedout");
- } else if (cpr->discard_body == NGX_HTTP_COPY_DISCARD_CHUNK_BODY) {
- ngx_log_error(NGX_LOG_ERR, c->log, 0,
- "[copy] discard chunk: read timedout");
- } else if (cpr->buffer == NULL) {
- ngx_log_error(NGX_LOG_ERR, c->log, 0,
- "[copy] recv response: timedout. No data has been read.");
- } else {
- if (cpr->process_header == ngx_http_copy_parse_status_line) {
- ngx_log_error(NGX_LOG_ERR, c->log, 0,
- "[copy] parse status line: read timedout.");
- } else {
- ngx_log_error(NGX_LOG_ERR, c->log, 0,
- "[copy] parse headers: read timedout");
- }
- ngx_log_error(NGX_LOG_INFO, c->log, 0,
- "[copy] read %d bytes data:\"%*s\"",
- cpr->buffer->last - cpr->buffer->start,
- cpr->buffer->last - cpr->buffer->start,
- cpr->buffer->start);
- }
- ngx_http_copy_finalize_request(cpr);
- return;
- }
- ngx_http_copy_discard_response(cpr);
- }
- static ngx_int_t
- ngx_http_copy_send_request(ngx_http_copy_request_t *cpr)
- {
- ngx_int_t rc;
- ngx_connection_t *c;
- c = cpr->peer.connection;
- c->log->action = "sending request";
- // TODO: action for serial
- if (!cpr->request_sent && ngx_http_copy_test_connect(c) != NGX_OK) {
- ngx_http_copy_finalize_request(cpr);
- return NGX_ERROR;
- }
- rc = ngx_output_chain(&cpr->output, cpr->request_sent ? NULL : cpr->request_bufs);
- cpr->request_sent = 1;
- if (rc == NGX_ERROR) {
- ngx_log_error(NGX_LOG_ERR, c->log, 0, "[copy] cannot send request to backend");
- ngx_http_copy_finalize_request(cpr);
- return NGX_ERROR;
- }
- if (c->write->timer_set) {
- ngx_del_timer(c->write);
- }
- if (rc == NGX_AGAIN) {
- ngx_add_timer(c->write, 5000); /* TODO: configure this value */
- if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
- ngx_http_copy_finalize_request(cpr);
- return NGX_ERROR;
- }
- return NGX_OK;
- }
- /* rc == NGX_OK */
- (void) ngx_atomic_fetch_add(©_status->request_count, 1);
- ngx_add_timer(c->read, 5000); /* TODO: configure this value */
- #if 0
- /* should not handle response, which has bad effect on performance */
- if (c->read->ready) {
- /* prehandle read event */
- ngx_log_error(NGX_LOG_INFO, c->log, 0, "[copy] readahead response");
- ngx_http_copy_recv_response_handler(c->read);
- return; /* why return? */
- }
- #endif
- c->write->handler = ngx_http_copy_dummy_handler;
- if(ngx_handle_write_event(c->write, 0) != NGX_OK) {
- ngx_http_copy_finalize_request(cpr);
- return NGX_ERROR;
- }
- return NGX_OK;
- }
- static ngx_int_t
- ngx_chain_buf_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in)
- {
- ngx_chain_t *cl, **ll;
- ll = chain;
- for (cl = *chain; cl; cl = cl->next) {
- ll = &cl->next;
- }
- while (in) {
- cl = ngx_alloc_chain_link(pool);
- if (cl == NULL) {
- return NGX_ERROR;
- }
- cl->buf = ngx_calloc_buf(pool);
- if (cl->buf == NULL) {
- return NGX_ERROR;
- }
- *cl->buf = *in->buf;
- *ll = cl;
- ll = &cl->next;
- in = in->next;
- }
- *ll = NULL;
- return NGX_OK;
- }
- static ngx_int_t
- ngx_http_copy_request(ngx_http_copy_request_t *cpr)
- {
- ngx_http_request_t *r = cpr->r; /* assert cpr->r != NULL*/
- ngx_buf_t *b;
- ngx_chain_t *cl;
- ngx_list_part_t *part;
- ngx_table_elt_t *header;
- ngx_uint_t i, force_keepalive;
- size_t len;
- /* change request to HTTP/1.1 and delete "Connection: ..." header */
- force_keepalive = cpr->cplcf->force_keepalive;
- /* calculate request(excluding body) length */
- len = r->method_name.len + 1 + r->unparsed_uri.len;
- /* http version length */
- if (force_keepalive || r->http_version == NGX_HTTP_VERSION_11) {
- len += sizeof(ngx_http_copy_version_11) - 1;
- } else {
- len += sizeof(ngx_http_copy_version) - 1;
- }
- /* header length */
- part = &r->headers_in.headers.part;
- header = part->elts;
- for (i = 0; /* void */; i++) {
- if (i >= part->nelts) {
- if (part->next == NULL) {
- break;
- }
- part = part->next;
- header = part->elts;
- i = 0;
- }
- if (force_keepalive
- && header[i].key.len == 10
- && ngx_strncmp(header[i].key.data, "Connection", 10) == 0)
- {
- continue;
- }
- len += header[i].key.len + sizeof(": ") - 1
- + header[i].value.len + sizeof(CRLF) - 1;
- }
- len += 2; /* header end "\r\n" */
- /* copy request */
- b = ngx_create_temp_buf(cpr->pool, len);
- if (b == NULL) {
- return NGX_ERROR;
- }
- cl = ngx_alloc_chain_link(cpr->pool);
- if (cl == NULL) {
- return NGX_ERROR;
- }
- cl->buf = b;
- /* copy request line */
- b->last = ngx_copy(b->last, r->method_name.data,
- r->method_name.len + 1/* space char */);
- len = r->method_name.len + 1;
- b->last = ngx_copy(b->last, r->unparsed_uri.data,
- r->unparsed_uri.len);
- len += r->unparsed_uri.len;
- if (force_keepalive || r->http_version == NGX_HTTP_VERSION_11) {
- b->last = ngx_cpymem(b->last, ngx_http_copy_version_11,
- sizeof(ngx_http_copy_version_11) - 1);
- len += sizeof(ngx_http_copy_version_11) - 1;
- } else {
- b->last = ngx_cpymem(b->last, ngx_http_copy_version,
- sizeof(ngx_http_copy_version) - 1);
- len += sizeof(ngx_http_copy_version) - 1;
- }
- /* copy headers */
- for (i = 0; /* void */; i++) {
- if (i >= part->nelts) {
- if (part->next == NULL) {
- break;
- }
- part = part->next;
- header = part->elts;
- i = 0;
- }
- if (force_keepalive
- && header[i].key.len == 10
- && ngx_strncmp(header[i].key.data, "Connection", 10) == 0)
- {
- continue;
- }
- b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
- len += header[i].key.len;
- *b->last++ = ':'; *b->last++ = ' ';
- len += 2;
- b->last = ngx_copy(b->last, header[i].value.data,
- header[i].value.len);
- len += header[i].value.len;
- *b->last++ = CR; *b->last++ = LF;
- len += 2;
- }
- /* add "\r\n" at the header end */
- *b->last++ = CR; *b->last++ = LF;
- len += 2;
- /* copy body */
- cpr->request_bufs = cl; /* cl -> status_lien & headers */
- b->flush = 1;
- cl->next = NULL;
- if (cpr->serial) {
- ngx_chain_buf_add_copy(cpr->pool, &cpr->serial_request_bufs,
- cpr->request_bufs);
- }
- return NGX_OK;
- }
- static ngx_int_t
- ngx_http_copy_connect(ngx_http_copy_request_t *cpr)
- {
- ngx_http_copy_loc_conf_t *cplcf = cpr->cplcf;
- ngx_peer_connection_t *pc;
- ngx_connection_t *c; /* connection to backend */
- ngx_int_t rc;
- pc = &cpr->peer;
- pc->log = ngx_cycle->log;
- pc->data = cpr;
- pc->get = ngx_http_copy_get_peer; /* get conn from cache if keepalive is on */
- rc = ngx_event_connect_peer(pc);
- if (rc == NGX_ERROR || rc == NGX_DECLINED || rc == NGX_BUSY) {
- /*
- * NGX_DECLINED: get new peer, but cannot connect it
- * NGX_BUSY: pc->get() return this value because of max_connections limit.
- * NGX_ERROR: cannot get peer or syscalls error
- */
- if (rc == NGX_DECLINED) {
- ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
- "[copy] cannot connect backend: connect() returns error");
- } else if (rc == NGX_BUSY) {
- ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
- "[copy] cannot connect backend: too many worker connections");
- } else {
- ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
- "[copy] cannot connect backend");
- }
- return NGX_ERROR;
- }
- /* rc == NGX_OK || rc == NGX_DONE (keepalive) || rc == NGX_AGAIN */
- cpr->connect = 1;
- (void) ngx_atomic_fetch_add(©_status->active_connect, 1);
- if (rc != NGX_DONE) {
- cplcf->connection++;
- } else {
- (void) ngx_atomic_fetch_add(©_status->active_connect_keepalive, 1);
- }
- c = pc->connection;
- c->data = cpr;
- c->write->handler = ngx_http_copy_send_request_handler;
- c->read->handler = ngx_http_copy_recv_response_handler;
- c->sendfile &= cpr->r->connection->sendfile;
- c->pool = cpr->pool;
- c->log = ngx_cycle->log; /* FIXME: better to alloc a new one */
- c->read->log = c->log;
- c->write->log = c->log;
- if (rc == NGX_AGAIN) {
- /*
- * ngx_event_connect_peer will add write event when rc == NGX_AGAIN
- * connected handler: log connect_count and send request
- */
- c->write->handler = ngx_http_copy_connected_handler;
- ngx_add_timer(c->write, 5000);
- } else {
- (void) ngx_atomic_fetch_add(©_status->connect_count, 1);
- }
- return rc;
- }
- static void
- ngx_http_copy_cleanup(void *data)
- {
- ngx_queue_t *copy_request, *q;
- ngx_http_copy_request_t *cpr;
- copy_request = data;
- while (!ngx_queue_empty(copy_request)) {
- q = ngx_queue_last(copy_request);
- cpr = ngx_queue_data(q, ngx_http_copy_request_t, queue);
- ngx_log_error(NGX_LOG_DEBUG, cpr->r->connection->log, 0,
- "[copy] cleanup: request \"%V\" disattach cpr %p",
- &cpr->r->uri, cpr);
- cpr->r = NULL;
- ngx_queue_remove(&cpr->queue);
- }
- }
- static ngx_int_t
- ngx_http_copy_init_request(ngx_http_copy_request_t *cpr)
- {
- ngx_http_core_loc_conf_t *clcf;
- ngx_int_t rc;
- ngx_http_copy_ctx_t *ctx;
- /* connect backend server */
- rc = ngx_http_copy_connect(cpr);
- if (rc == NGX_ERROR) {
- ngx_http_copy_finalize_request(cpr);
- return NGX_ERROR;
- }
- /* copy request */
- if (ngx_http_copy_request(cpr) == NGX_ERROR) {
- ngx_http_copy_finalize_request(cpr);
- return NGX_ERROR;
- }
- /* handle incoming body */
- if (1 /* TODO: detect whether there is body data */
- && cpr->r
- && ngx_queue_empty(&cpr->queue))
- {
- ctx = ngx_http_get_module_ctx(cpr->r, ngx_http_copy_module);
- ngx_queue_insert_head(&ctx->copy_request, &cpr->queue);
- }
- /* set output chain context */
- clcf = ngx_http_get_module_loc_conf(cpr->r, ngx_http_core_module);
- cpr->output.alignment = clcf->directio_alignment;
- cpr->output.pool = cpr->pool;
- cpr->output.bufs.num = 1;
- cpr->output.bufs.size = clcf->client_body_buffer_size;
- cpr->output.output_filter = ngx_chain_writer;
- cpr->output.filter_ctx = &cpr->writer;
- /* writer.out .. *writer.last are data waiting to send */
- cpr->writer.out = NULL;
- cpr->writer.last = &cpr->writer.out;
- cpr->writer.connection = cpr->peer.connection;
- cpr->writer.limit = 0;
- cpr->writer.pool = cpr->pool;
- cpr->request_sent = 0;
- if (rc == NGX_AGAIN) {
- return NGX_OK;
- }
- /* send request, rc == NGX_DONE || rc == NGX_OK */
- return ngx_http_copy_send_request(cpr);
- }
- static ngx_http_copy_request_t *
- ngx_http_copy_alloc_request(ngx_http_request_t *r, ngx_http_copy_loc_conf_t *cplcf)
- {
- ngx_pool_t *pool;
- ngx_http_copy_request_t *cpr;
- /* create request (copied from @r) */
- pool = ngx_create_pool(512, ngx_cycle->log);
- if (pool == NULL) {
- return NULL;
- }
- cpr = ngx_pcalloc(pool, sizeof(ngx_http_copy_request_t));
- if (cpr == NULL) {
- ngx_destroy_pool(pool);
- return NULL;
- }
- cpr->r = r; /* can only read r, shouldnt modify r */
- cpr->cplcf = cplcf; /* used when response is sent back */
- cpr->length = -1; /* response length */
- cpr->pool = pool;
- cpr->process_header = ngx_http_copy_parse_status_line;
- ngx_queue_init(&cpr->queue);
- if (cplcf->serial && r->method == NGX_HTTP_GET) {
- cpr->serial = 1;
- }
- return cpr;
- }
- static void
- ngx_http_copy_init_requests(ngx_http_request_t *r)
- {
- ngx_http_copy_request_t *cpr; /* request copied from @r */
- ngx_http_copy_loc_conf_t *cplcf;
- ngx_http_copy_ctx_t *ctx;
- ngx_http_cleanup_t *cln;
- ngx_uint_t i;
- cplcf = ngx_http_get_module_loc_conf(r, ngx_http_copy_module);
- /* init copy context */
- ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_copy_ctx_t));
- if (ctx == NULL) {
- return;
- }
- ngx_queue_init(&ctx->copy_request);
- ngx_http_set_ctx(r, ctx, ngx_http_copy_module);
- /* add cleanup handler */
- cln = ngx_http_cleanup_add(r, 0 /* zero data size*/);
- if (cln == NULL) {
- return;
- }
- cln->handler = ngx_http_copy_cleanup; /* called in ngx_http_free_request() */
- cln->data = &ctx->copy_request;
- for (i = 0; i < cplcf->multiple; i++) {
- /* create request (copied from @r) */
- cpr = ngx_http_copy_alloc_request(r, cplcf);
- if (cpr == NULL) {
- return;
- }
- if (ngx_http_copy_init_request(cpr) == NGX_ERROR) {
- ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
- "[copy] should send %d requests, only complete %d requests",
- cplcf->multiple, i);
- break;
- }
- if (cpr->serial) {
- ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
- "[copy] serial: first copied request has been prepared");
- break;
- }
- }
- }
- static ngx_int_t
- ngx_http_copy_handler(ngx_http_request_t *r)
- {
- ngx_http_copy_loc_conf_t *cplcf;
- cplcf = ngx_http_get_module_loc_conf(r, ngx_http_copy_module);
- if (cplcf->on) {
- ngx_http_copy_init_requests(r);
- }
- /* let original request continue to run */
- return NGX_DECLINED;
- }
- static char *
- ngx_http_copy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
- {
- ngx_http_copy_loc_conf_t *prev = parent;
- ngx_http_copy_loc_conf_t *conf = child;
- ngx_conf_merge_ptr_value(conf->addrs, prev->addrs, NULL);
- ngx_conf_merge_uint_value(conf->naddrs, prev->naddrs, (ngx_uint_t)0);
- ngx_conf_merge_uint_value(conf->multiple, prev->multiple, 1);
- if (ngx_queue_empty(&prev->cache_connections)) {
- ngx_queue_init(&conf->cache_connections);
- } else {
- conf->cache_connections = prev->cache_connections;
- }
- ngx_conf_merge_value(conf->max_cached, prev->max_cached, (ngx_int_t)65535);
- ngx_conf_merge_value(conf->cached, prev->cached, (ngx_int_t)0);
- ngx_conf_merge_msec_value(conf->cached_timeout,
- prev->cached_timeout, 60000);
- ngx_conf_merge_value(conf->max_connection, prev->max_connection, (ngx_int_t)65535);
- ngx_conf_merge_value(conf->connection, prev->connection, (ngx_int_t)0);
- ngx_conf_merge_value(conf->on, prev->on, 0);
- ngx_conf_merge_value(conf->keepalive, prev->keepalive, 1);
- ngx_conf_merge_value(conf->force_keepalive, prev->force_keepalive, 1);
- ngx_conf_merge_value(conf->serial, prev->serial, 0);
- return NGX_CONF_OK;
- }
- static void *
- ngx_http_copy_create_loc_conf(ngx_conf_t *cf)
- {
- ngx_http_copy_loc_conf_t *cplcf;
- cplcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_copy_loc_conf_t));
- if (cplcf == NULL) {
- return NULL;
- }
- /* keepalive is disabled by default */
- /* NOTE: NGX_CONF_UNSET_<TYPE> used by ngx_conf_merge_<type>_value */
- cplcf->addrs = NGX_CONF_UNSET_PTR;
- cplcf->naddrs = NGX_CONF_UNSET_UINT;
- cplcf->multiple = NGX_CONF_UNSET_UINT;
- ngx_queue_init(&cplcf->cache_connections);
- cplcf->max_cached = NGX_CONF_UNSET;
- cplcf->cached = NGX_CONF_UNSET;
- cplcf->cached_timeout = NGX_CONF_UNSET_MSEC;
- cplcf->max_connection = NGX_CONF_UNSET;
- cplcf->connection = NGX_CONF_UNSET;
- cplcf->on = NGX_CONF_UNSET;
- cplcf->keepalive = NGX_CONF_UNSET;
- cpl