static ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data) { ngx_uint_t port; v->len = 0; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; if (ngx_connection_local_sockaddr(s->connection, NULL, 0) != NGX_OK) { return NGX_ERROR; } v->data = ngx_pnalloc(s->connection->pool, sizeof("65535") - 1); if (v->data == NULL) { return NGX_ERROR; } port = ngx_inet_get_port(s->connection->local_sockaddr); if (port > 0 && port < 65536) { v->len = ngx_sprintf(v->data, "%ui", port) - v->data; } return NGX_OK; }
static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { ngx_str_t s; u_char addr[NGX_SOCKADDR_STRLEN]; s.len = NGX_SOCKADDR_STRLEN; s.data = addr; if (ngx_connection_local_sockaddr(r->connection, &s, 0) != NGX_OK) { return NGX_ERROR; } s.data = ngx_pnalloc(r->pool, s.len); if (s.data == NULL) { return NGX_ERROR; } ngx_memcpy(s.data, addr, s.len); v->len = s.len; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; v->data = s.data; return NGX_OK; }
static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data) { ngx_str_t str; u_char addr[NGX_SOCKADDR_STRLEN]; str.len = NGX_SOCKADDR_STRLEN; str.data = addr; if (ngx_connection_local_sockaddr(s->connection, &str, 0) != NGX_OK) { return NGX_ERROR; } str.data = ngx_pnalloc(s->connection->pool, str.len); if (str.data == NULL) { return NGX_ERROR; } ngx_memcpy(str.data, addr, str.len); v->len = str.len; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; v->data = str.data; return NGX_OK; }
u_char * ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last) { ngx_uint_t port, lport; if (last - buf < NGX_PROXY_PROTOCOL_MAX_HEADER) { return NULL; } if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { return NULL; } switch (c->sockaddr->sa_family) { case AF_INET: buf = ngx_cpymem(buf, "PROXY TCP4 ", sizeof("PROXY TCP4 ") - 1); port = ntohs(((struct sockaddr_in *) c->sockaddr)->sin_port); lport = ntohs(((struct sockaddr_in *) c->local_sockaddr)->sin_port); break; #if (NGX_HAVE_INET6) case AF_INET6: buf = ngx_cpymem(buf, "PROXY TCP6 ", sizeof("PROXY TCP6 ") - 1); port = ntohs(((struct sockaddr_in6 *) c->sockaddr)->sin6_port); lport = ntohs(((struct sockaddr_in6 *) c->local_sockaddr)->sin6_port); break; #endif default: return ngx_cpymem(buf, "PROXY UNKNOWN" CRLF, sizeof("PROXY UNKNOWN" CRLF) - 1); } buf += ngx_sock_ntop(c->sockaddr, c->socklen, buf, last - buf, 0); *buf++ = ' '; buf += ngx_sock_ntop(c->local_sockaddr, c->local_socklen, buf, last - buf, 0); return ngx_slprintf(buf, last, " %ui %ui" CRLF, port, lport); }
static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { ngx_uint_t port; struct sockaddr_in *sin; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; #endif v->len = 0; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; if (ngx_connection_local_sockaddr(r->connection, NULL, 0) != NGX_OK) { return NGX_ERROR; } v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1); if (v->data == NULL) { return NGX_ERROR; } switch (r->connection->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) r->connection->local_sockaddr; port = ntohs(sin6->sin6_port); break; #endif default: /* AF_INET */ sin = (struct sockaddr_in *) r->connection->local_sockaddr; port = ntohs(sin->sin_port); break; } if (port > 0 && port < 65536) { v->len = ngx_sprintf(v->data, "%ui", port) - v->data; } return NGX_OK; }
void ngx_rtmp_init_connection(ngx_connection_t *c) { ngx_uint_t i; ngx_rtmp_port_t *port; struct sockaddr *sa; struct sockaddr_in *sin; ngx_rtmp_in_addr_t *addr; ngx_rtmp_session_t *s; ngx_rtmp_addr_conf_t *addr_conf; ngx_int_t unix_socket; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; ngx_rtmp_in6_addr_t *addr6; #endif ++ngx_rtmp_naccepted; /* find the server configuration for the address:port */ /* AF_INET only */ port = c->listening->servers; unix_socket = 0; if (port->naddrs > 1) { /* * There are several addresses on this port and one of them * is the "*:port" wildcard so getsockname() is needed to determine * the server address. * * AcceptEx() already gave this address. */ if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { ngx_rtmp_close_connection(c); return; } sa = c->local_sockaddr; switch (sa->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) sa; addr6 = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { break; } } addr_conf = &addr6[i].conf; break; #endif case AF_UNIX: unix_socket = 1; default: /* AF_INET */ sin = (struct sockaddr_in *) sa; addr = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (addr[i].addr == sin->sin_addr.s_addr) { break; } } addr_conf = &addr[i].conf; break; } } else { switch (c->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: addr6 = port->addrs; addr_conf = &addr6[0].conf; break; #endif case AF_UNIX: unix_socket = 1; default: /* AF_INET */ addr = port->addrs; addr_conf = &addr[0].conf; break; } } ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%ui client connected '%V'", c->number, &c->addr_text); s = ngx_rtmp_init_session(c, addr_conf); /* only auto-pushed connections are * done through unix socket */ s->auto_pushed = unix_socket; if (s) { ngx_rtmp_handshake(s); } }
void ngx_tcp_init_connection(ngx_connection_t *c) { ngx_uint_t i; ngx_tcp_port_t *port; struct sockaddr *sa; struct sockaddr_in *sin; ngx_tcp_log_ctx_t *ctx; ngx_tcp_in_addr_t *addr; ngx_tcp_session_t *s; ngx_tcp_addr_conf_t *addr_conf; ngx_tcp_core_srv_conf_t *cscf; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; ngx_tcp_in6_addr_t *addr6; #endif /* find the server configuration for the address:port */ port = c->listening->servers; if (port && port->naddrs > 1) { /* * There are several addresses on this port and one of them * is the "*:port" wildcard so getsockname() is needed to determine * the server address. * * AcceptEx() already gave this address. */ if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { ngx_tcp_close_connection(c); return; } sa = c->local_sockaddr; switch (sa->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) sa; addr6 = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { break; } } addr_conf = &addr6[i].conf; break; #endif default: /* AF_INET */ sin = (struct sockaddr_in *) sa; addr = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (addr[i].addr == sin->sin_addr.s_addr) { break; } } addr_conf = &addr[i].conf; break; } } else { switch (c->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: addr6 = port->addrs; addr_conf = &addr6[0].conf; break; #endif case AF_INET: addr = port->addrs; addr_conf = &addr[0].conf; break; default: /* AF_UNIX */ addr = port->addrs; addr_conf = &addr[0].conf; break; } } cscf = ngx_tcp_get_module_srv_conf(addr_conf->ctx, ngx_tcp_core_module); s = cscf->protocol->create_session(c); if (s == NULL) { ngx_tcp_close_connection(c); return; } s->main_conf = addr_conf->ctx->main_conf; s->srv_conf = addr_conf->ctx->srv_conf; s->addr_text = &addr_conf->addr_text; c->data = s; s->connection = c; ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%ui client %V connected to %V", c->number, &c->addr_text, s->addr_text); ctx = ngx_palloc(c->pool, sizeof(ngx_tcp_log_ctx_t)); if (ctx == NULL) { ngx_tcp_close_connection(c); return; } ctx->client = &c->addr_text; ctx->session = s; c->log->connection = c->number; c->log->handler = ngx_tcp_log_error_msg; c->log->data = ctx; c->log->action = "sending client greeting line"; c->log_error = NGX_ERROR_INFO; #if (NGX_TCP_SSL) { ngx_tcp_ssl_conf_t *sslcf; sslcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_ssl_module); if (sslcf->enable) { c->log->action = "SSL handshaking"; ngx_tcp_ssl_init_connection(&sslcf->ssl, c); return; } if (addr_conf->ssl) { c->log->action = "SSL handshaking"; if (sslcf->ssl.ctx == NULL) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "no \"ssl_certificate\" is defined " "in server listening on SSL port"); ngx_tcp_close_connection(c); return; } ngx_tcp_ssl_init_connection(&sslcf->ssl, c); return; } } #endif ngx_tcp_init_session(c); }
void ngx_tcp2http_init_connection(ngx_connection_t *c) { ngx_uint_t i; ngx_event_t *rev; struct sockaddr_in *sin; ngx_http_port_t *port; ngx_http_in_addr_t *addr; ngx_http_log_ctx_t *ctx; ngx_http_connection_t *hc; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; ngx_http_in6_addr_t *addr6; #endif hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t)); if (hc == NULL) { ngx_http_close_connection(c); return; } c->data = hc; /* find the server configuration for the address:port */ port = c->listening->servers; if (port->naddrs > 1) { /* * there are several addresses on this port and one of them * is an "*:port" wildcard so getsockname() in ngx_http_server_addr() * is required to determine a server address */ if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { ngx_http_close_connection(c); return; } switch (c->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) c->local_sockaddr; addr6 = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { break; } } hc->addr_conf = &addr6[i].conf; break; #endif default: /* AF_INET */ sin = (struct sockaddr_in *) c->local_sockaddr; addr = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (addr[i].addr == sin->sin_addr.s_addr) { break; } } hc->addr_conf = &addr[i].conf; break; } } else { switch (c->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: addr6 = port->addrs; hc->addr_conf = &addr6[0].conf; break; #endif default: /* AF_INET */ addr = port->addrs; hc->addr_conf = &addr[0].conf; break; } } /* the default server configuration for the address:port */ hc->conf_ctx = hc->addr_conf->default_server->ctx; ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); if (ctx == NULL) { ngx_http_close_connection(c); return; } ctx->connection = c; ctx->request = NULL; ctx->current_request = NULL; c->log->connection = c->number; c->log->handler = ngx_tcp2http_log_error; c->log->data = ctx; c->log->action = "waiting for request"; c->log_error = NGX_ERROR_INFO; rev = c->read; rev->handler = ngx_tcp2http_init_session; rev->data=c; c->write->handler = ngx_tcp2http_empty_handler; /* #if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); #endif */ if (rev->ready) { /* the deferred accept(), rtsig, aio, iocp */ if (ngx_use_accept_mutex) { ngx_post_event(rev, &ngx_posted_events); return; } rev->handler(rev); return; } ngx_add_timer(rev, c->listening->post_accept_timeout); if (ngx_handle_read_event(rev, 0) != NGX_OK) { /* #if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); #endif */ ngx_http_close_connection(c); return; } }
static void ngx_limit_tcp_accepted(ngx_event_t *ev) { ngx_limit_tcp_accept_ctx_t *actx = ev->data; ngx_int_t rc, idx; ngx_uint_t excess, delay_excess; ngx_msec_t delay_time; ngx_listening_t *ls; ngx_connection_t *c; ngx_pool_cleanup_t *cln; ngx_limit_tcp_ctx_t *ctx; ngx_limit_tcp_node_t *node; ngx_limit_tcp_delay_ctx_t *dctx; ngx_limit_tcp_clean_ctx_t *cctx; ngx_limit_tcp_listen_ctx_t *lctx; c = actx->connection; ls = c->listening; lctx = actx->lctx; ngx_log_debug0(NGX_LOG_DEBUG_CORE, ev->log, 0, "limit tcp accepted"); if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { ngx_close_accepted_connection(c); return; } idx = ngx_limit_tcp_get_addr_index(ls, c->local_sockaddr, 0); ngx_log_debug2(NGX_LOG_DEBUG_CORE, ev->log, 0, "accept listen %V idx: %i", &ls->addr_text, idx); if (idx == NGX_ERROR || lctx->addrs[idx] == NULL) { goto accept_continue; } ctx = lctx->addrs[idx]->ctx; rc = ngx_limit_tcp_find(c); if (rc == NGX_BUSY) { ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "limit %V find in black list", &c->addr_text); ngx_close_accepted_connection(c); return; } else if (rc == NGX_DECLINED) { ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "limit %V find in white list", &c->addr_text); goto accept_continue; } delay_excess = 0; ngx_shmtx_lock(&ctx->shpool->mutex); ngx_limit_tcp_expire(c, ctx, 1); excess = 0; node = 0; rc = ngx_limit_tcp_lookup(c, ctx, &excess, &node); ngx_shmtx_unlock(&ctx->shpool->mutex); if (rc == NGX_ERROR || node == 0) { goto accept_continue; } if (rc == NGX_BUSY) { ngx_close_accepted_connection(c); return; } cctx = ngx_pcalloc(c->pool, sizeof(ngx_limit_tcp_clean_ctx_t)); if (cctx == NULL) { ngx_close_accepted_connection(c); return; } cctx->node = node; cctx->connection = c; cln = ngx_pool_cleanup_add(c->pool, 0); if (cln == NULL) { ngx_close_accepted_connection(c); return; } cln->handler = ngx_limit_tcp_cleanup; cln->data = cctx; if (rc == NGX_AGAIN) { if (delay_excess < excess) { delay_excess = excess; } } if (delay_excess) { if (ctx->nodelay) { goto accept_continue; } delay_time = (ngx_msec_t) delay_excess * 1000 / ctx->rate; if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_close_accepted_connection(c); return; } dctx = ngx_pcalloc(c->pool, sizeof(ngx_limit_tcp_delay_ctx_t)); if (dctx == NULL) { ngx_close_accepted_connection(c); return; } dctx->connection = c; dctx->rdata = c->read->data; dctx->rhandler = c->read->handler; dctx->whandler = c->write->handler; dctx->wdata = c->write->data; dctx->handler = ls->handler; c->read->data = dctx; c->write->data = dctx; c->read->handler = ngx_limit_tcp_test_reading; c->write->handler = ngx_limit_tcp_delay; ngx_add_timer(c->write, delay_time); return; } accept_continue: ls->handler(c); }
static ngx_int_t ngx_http_v2_header_filter(ngx_http_request_t *r) { u_char status, *pos, *start, *p, *tmp; size_t len, tmp_len; ngx_str_t host, location; ngx_uint_t i, port; ngx_list_part_t *part; ngx_table_elt_t *header; ngx_connection_t *fc; ngx_http_cleanup_t *cln; ngx_http_v2_out_frame_t *frame; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; u_char addr[NGX_SOCKADDR_STRLEN]; static const u_char nginx[5] = "\x84\xaa\x63\x55\xe7"; #if (NGX_HTTP_GZIP) static const u_char accept_encoding[12] = "\x8b\x84\x84\x2d\x69\x5b\x05\x44\x3c\x86\xaa\x6f"; #endif static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER); static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)]; if (!r->stream) { return ngx_http_next_header_filter(r); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http2 header filter"); if (r->header_sent) { return NGX_OK; } r->header_sent = 1; if (r != r->main) { return NGX_OK; } if (r->method == NGX_HTTP_HEAD) { r->header_only = 1; } switch (r->headers_out.status) { case NGX_HTTP_OK: status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_200_INDEX); break; case NGX_HTTP_NO_CONTENT: r->header_only = 1; ngx_str_null(&r->headers_out.content_type); r->headers_out.content_length = NULL; r->headers_out.content_length_n = -1; r->headers_out.last_modified_time = -1; r->headers_out.last_modified = NULL; status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_204_INDEX); break; case NGX_HTTP_PARTIAL_CONTENT: status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_206_INDEX); break; case NGX_HTTP_NOT_MODIFIED: r->header_only = 1; status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_304_INDEX); break; default: r->headers_out.last_modified_time = -1; r->headers_out.last_modified = NULL; switch (r->headers_out.status) { case NGX_HTTP_BAD_REQUEST: status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_400_INDEX); break; case NGX_HTTP_NOT_FOUND: status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_404_INDEX); break; case NGX_HTTP_INTERNAL_SERVER_ERROR: status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_500_INDEX); break; default: status = 0; } } len = status ? 1 : 1 + ngx_http_v2_literal_size("418"); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (r->headers_out.server == NULL) { len += 1 + (clcf->server_tokens ? nginx_ver_len : sizeof(nginx)); } if (r->headers_out.date == NULL) { len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT"); } if (r->headers_out.content_type.len) { len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.content_type.len; if (r->headers_out.content_type_len == r->headers_out.content_type.len && r->headers_out.charset.len) { len += sizeof("; charset=") - 1 + r->headers_out.charset.len; } } if (r->headers_out.content_length == NULL && r->headers_out.content_length_n >= 0) { len += 1 + ngx_http_v2_integer_octets(NGX_OFF_T_LEN) + NGX_OFF_T_LEN; } if (r->headers_out.last_modified == NULL && r->headers_out.last_modified_time != -1) { len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT"); } fc = r->connection; if (r->headers_out.location && r->headers_out.location->value.len) { if (r->headers_out.location->value.data[0] == '/') { if (clcf->server_name_in_redirect) { cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); host = cscf->server_name; } else if (r->headers_in.server.len) { host = r->headers_in.server; } else { host.len = NGX_SOCKADDR_STRLEN; host.data = addr; if (ngx_connection_local_sockaddr(fc, &host, 0) != NGX_OK) { return NGX_ERROR; } } port = ngx_inet_get_port(fc->local_sockaddr); location.len = sizeof("https://") - 1 + host.len + r->headers_out.location->value.len; if (clcf->port_in_redirect) { #if (NGX_HTTP_SSL) if (fc->ssl) port = (port == 443) ? 0 : port; else #endif port = (port == 80) ? 0 : port; } else { port = 0; } if (port) { location.len += sizeof(":65535") - 1; } location.data = ngx_pnalloc(r->pool, location.len); if (location.data == NULL) { return NGX_ERROR; } p = ngx_cpymem(location.data, "http", sizeof("http") - 1); #if (NGX_HTTP_SSL) if (fc->ssl) { *p++ = 's'; } #endif *p++ = ':'; *p++ = '/'; *p++ = '/'; p = ngx_cpymem(p, host.data, host.len); if (port) { p = ngx_sprintf(p, ":%ui", port); } p = ngx_cpymem(p, r->headers_out.location->value.data, r->headers_out.location->value.len); /* update r->headers_out.location->value for possible logging */ r->headers_out.location->value.len = p - location.data; r->headers_out.location->value.data = location.data; ngx_str_set(&r->headers_out.location->key, "Location"); } r->headers_out.location->hash = 0; len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.location->value.len; } tmp_len = len; #if (NGX_HTTP_GZIP) if (r->gzip_vary) { if (clcf->gzip_vary) { len += 1 + sizeof(accept_encoding); } else { r->gzip_vary = 0; } } #endif part = &r->headers_out.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 (header[i].hash == 0) { continue; } if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { ngx_log_error(NGX_LOG_CRIT, fc->log, 0, "too long response header name: \"%V\"", &header[i].key); return NGX_ERROR; } if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { ngx_log_error(NGX_LOG_CRIT, fc->log, 0, "too long response header value: \"%V: %V\"", &header[i].key, &header[i].value); return NGX_ERROR; } len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; if (header[i].key.len > tmp_len) { tmp_len = header[i].key.len; } if (header[i].value.len > tmp_len) { tmp_len = header[i].value.len; } } tmp = ngx_palloc(r->pool, tmp_len); pos = ngx_pnalloc(r->pool, len); if (pos == NULL || tmp == NULL) { return NGX_ERROR; } start = pos; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 output header: \":status: %03ui\"", r->headers_out.status); if (status) { *pos++ = status; } else { *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX); *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3; pos = ngx_sprintf(pos, "%03ui", r->headers_out.status); } if (r->headers_out.server == NULL) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 output header: \"server: %s\"", clcf->server_tokens ? NGINX_VER : "nginx"); *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX); if (clcf->server_tokens) { if (nginx_ver[0] == '\0') { p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER, sizeof(NGINX_VER) - 1, tmp); nginx_ver_len = p - nginx_ver; } pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len); } else { pos = ngx_cpymem(pos, nginx, sizeof(nginx)); } } if (r->headers_out.date == NULL) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 output header: \"date: %V\"", &ngx_cached_http_time); *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX); pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data, ngx_cached_http_time.len, tmp); } if (r->headers_out.content_type.len) { *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX); if (r->headers_out.content_type_len == r->headers_out.content_type.len && r->headers_out.charset.len) { len = r->headers_out.content_type.len + sizeof("; charset=") - 1 + r->headers_out.charset.len; p = ngx_pnalloc(r->pool, len); if (p == NULL) { return NGX_ERROR; } p = ngx_cpymem(p, r->headers_out.content_type.data, r->headers_out.content_type.len); p = ngx_cpymem(p, "; charset=", sizeof("; charset=") - 1); p = ngx_cpymem(p, r->headers_out.charset.data, r->headers_out.charset.len); /* updated r->headers_out.content_type is also needed for logging */ r->headers_out.content_type.len = len; r->headers_out.content_type.data = p - len; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 output header: \"content-type: %V\"", &r->headers_out.content_type); pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data, r->headers_out.content_type.len, tmp); } if (r->headers_out.content_length == NULL && r->headers_out.content_length_n >= 0) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 output header: \"content-length: %O\"", r->headers_out.content_length_n); *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX); p = pos; pos = ngx_sprintf(pos + 1, "%O", r->headers_out.content_length_n); *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1); } if (r->headers_out.last_modified == NULL && r->headers_out.last_modified_time != -1) { *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX); ngx_http_time(pos, r->headers_out.last_modified_time); len = sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 output header: \"last-modified: %*s\"", len, pos); /* * Date will always be encoded using huffman in the temporary buffer, * so it's safe here to use src and dst pointing to the same address. */ pos = ngx_http_v2_write_value(pos, pos, len, tmp); } if (r->headers_out.location && r->headers_out.location->value.len) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 output header: \"location: %V\"", &r->headers_out.location->value); *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX); pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data, r->headers_out.location->value.len, tmp); } #if (NGX_HTTP_GZIP) if (r->gzip_vary) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 output header: \"vary: Accept-Encoding\""); *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX); pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding)); } #endif part = &r->headers_out.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 (header[i].hash == 0) { continue; } #if (NGX_DEBUG) if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) { ngx_strlow(tmp, header[i].key.data, header[i].key.len); ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 output header: \"%*s: %V\"", header[i].key.len, tmp, &header[i].value); } #endif *pos++ = 0; pos = ngx_http_v2_write_name(pos, header[i].key.data, header[i].key.len, tmp); pos = ngx_http_v2_write_value(pos, header[i].value.data, header[i].value.len, tmp); } frame = ngx_http_v2_create_headers_frame(r, start, pos); if (frame == NULL) { return NGX_ERROR; } ngx_http_v2_queue_blocked_frame(r->stream->connection, frame); cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { return NGX_ERROR; } cln->handler = ngx_http_v2_filter_cleanup; cln->data = r->stream; r->stream->queued = 1; fc->send_chain = ngx_http_v2_send_chain; fc->need_last_buf = 1; return ngx_http_v2_filter_send(fc, r->stream); }
static ngx_http_operationid_ctx_t * ngx_http_operationid_get_operationid(ngx_http_request_t *r, ngx_http_operationid_loc_conf_t *conf) { ngx_http_operationid_ctx_t *ctx; ngx_uint_t found=0; ngx_uint_t i; ngx_list_part_t *part = NULL; ngx_table_elt_t *header = NULL; ngx_connection_t *c; struct sockaddr_in *sin; uint32_t rid[4]; u_char *rid_as_pc; ctx = ngx_http_get_module_ctx(r, ngx_http_operationid_module); if (ctx) { return ctx; } if (ctx == NULL) { ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_operationid_ctx_t)); if (ctx == NULL) { return NULL; } ngx_http_set_ctx(r, ctx, ngx_http_operationid_module); } /* look for operation id header in request */ part = &r->headers_in.headers.part; header = part->elts; for ( i = 0 ; ; i++ ) { if ( i >= part->nelts) { if ( part->next == NULL ) { break; } part = part->next; header = part->elts; i = 0; } if ( ngx_strcmp(header[i].key.data, conf->header_name.data) == 0 ) { ctx->operationid = header[i].value.data; found = 1; break; } } if ( found ) { return ctx; } c = r->connection; if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { return NULL; } sin = (struct sockaddr_in *) c->local_sockaddr; rid[0] = sin->sin_addr.s_addr; rid[1] = htonl((uint32_t) ngx_time()); rid[2] = htonl(start_value); rid[3] = htonl(sequencer_v2); sequencer_v2 += 0x100; if (sequencer_v2 < 0x03030302) { sequencer_v2 = 0x03030302; } ctx->operationid = (u_char*) ngx_pcalloc(r->pool, 33); ctx->operationid[32] = 0; rid_as_pc = (u_char *) rid; for(i=0; i<16; i++) { ctx->operationid[2*i] = hex[rid_as_pc[i] >> 4]; ctx->operationid[2*i+1] = hex[rid_as_pc[i] & 0xf]; } return ctx; }
static ngx_int_t ngx_http_v2_header_filter(ngx_http_request_t *r) { u_char status, *pos, *start, *p; size_t len; ngx_str_t host, location; ngx_uint_t i, port; ngx_list_part_t *part; ngx_table_elt_t *header; ngx_connection_t *fc; ngx_http_cleanup_t *cln; ngx_http_v2_out_frame_t *frame; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; struct sockaddr_in *sin; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; #endif u_char addr[NGX_SOCKADDR_STRLEN]; if (!r->stream) { return ngx_http_next_header_filter(r); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http2 header filter"); if (r->header_sent) { return NGX_OK; } r->header_sent = 1; if (r != r->main) { return NGX_OK; } if (r->method == NGX_HTTP_HEAD) { r->header_only = 1; } switch (r->headers_out.status) { case NGX_HTTP_OK: status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_200_INDEX); break; case NGX_HTTP_NO_CONTENT: r->header_only = 1; ngx_str_null(&r->headers_out.content_type); r->headers_out.content_length = NULL; r->headers_out.content_length_n = -1; r->headers_out.last_modified_time = -1; r->headers_out.last_modified = NULL; status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_204_INDEX); break; case NGX_HTTP_PARTIAL_CONTENT: status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_206_INDEX); break; case NGX_HTTP_NOT_MODIFIED: r->header_only = 1; status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_304_INDEX); break; default: r->headers_out.last_modified_time = -1; r->headers_out.last_modified = NULL; switch (r->headers_out.status) { case NGX_HTTP_BAD_REQUEST: status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_400_INDEX); break; case NGX_HTTP_NOT_FOUND: status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_404_INDEX); break; case NGX_HTTP_INTERNAL_SERVER_ERROR: status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_500_INDEX); break; default: status = 0; } } len = status ? 1 : 1 + ngx_http_v2_literal_size("418"); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (r->headers_out.server == NULL) { len += 1 + clcf->server_tokens ? ngx_http_v2_literal_size(TENGINE_VER) : ngx_http_v2_literal_size("tengine"); } if (r->headers_out.date == NULL) { len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT"); } if (r->headers_out.content_type.len) { len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.content_type.len; if (r->headers_out.content_type_len == r->headers_out.content_type.len && r->headers_out.charset.len) { len += sizeof("; charset=") - 1 + r->headers_out.charset.len; } } if (r->headers_out.content_length == NULL && r->headers_out.content_length_n >= 0) { len += 1 + ngx_http_v2_integer_octets(NGX_OFF_T_LEN) + NGX_OFF_T_LEN; } if (r->headers_out.last_modified == NULL && r->headers_out.last_modified_time != -1) { len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT"); } fc = r->connection; if (r->headers_out.location && r->headers_out.location->value.len) { if (r->headers_out.location->value.data[0] == '/') { if (clcf->server_name_in_redirect) { cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); host = cscf->server_name; } else if (r->headers_in.server.len) { host = r->headers_in.server; } else { host.len = NGX_SOCKADDR_STRLEN; host.data = addr; if (ngx_connection_local_sockaddr(fc, &host, 0) != NGX_OK) { return NGX_ERROR; } } switch (fc->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) fc->local_sockaddr; port = ntohs(sin6->sin6_port); break; #endif #if (NGX_HAVE_UNIX_DOMAIN) case AF_UNIX: port = 0; break; #endif default: /* AF_INET */ sin = (struct sockaddr_in *) fc->local_sockaddr; port = ntohs(sin->sin_port); break; } location.len = sizeof("https://") - 1 + host.len + r->headers_out.location->value.len; if (clcf->port_in_redirect) { #if (NGX_HTTP_SSL) if (fc->ssl) port = (port == 443) ? 0 : port; else #endif port = (port == 80) ? 0 : port; } else { port = 0; } if (port) { location.len += sizeof(":65535") - 1; } location.data = ngx_pnalloc(r->pool, location.len); if (location.data == NULL) { return NGX_ERROR; } p = ngx_cpymem(location.data, "http", sizeof("http") - 1); #if (NGX_HTTP_SSL) if (fc->ssl) { *p++ = 's'; } #endif *p++ = ':'; *p++ = '/'; *p++ = '/'; p = ngx_cpymem(p, host.data, host.len); if (port) { p = ngx_sprintf(p, ":%ui", port); } p = ngx_cpymem(p, r->headers_out.location->value.data, r->headers_out.location->value.len); /* update r->headers_out.location->value for possible logging */ r->headers_out.location->value.len = p - location.data; r->headers_out.location->value.data = location.data; ngx_str_set(&r->headers_out.location->key, "Location"); } r->headers_out.location->hash = 0; len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.location->value.len; } #if (NGX_HTTP_GZIP) if (r->gzip_vary) { if (clcf->gzip_vary) { len += 1 + ngx_http_v2_literal_size("Accept-Encoding"); } else { r->gzip_vary = 0; } } #endif part = &r->headers_out.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 (header[i].hash == 0) { continue; } if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, "too long response header name: \"%V\"", &header[i].key); return NGX_ERROR; } if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, "too long response header value: \"%V: %V\"", &header[i].key, &header[i].value); return NGX_ERROR; } len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; } pos = ngx_palloc(r->pool, len); if (pos == NULL) { return NGX_ERROR; } start = pos; if (status) { *pos++ = status; } else { *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX); *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3; pos = ngx_sprintf(pos, "%03ui", r->headers_out.status); } if (r->headers_out.server == NULL) { *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX); if (clcf->server_tokens) { *pos++ = NGX_HTTP_V2_ENCODE_RAW | (sizeof(TENGINE_VER) - 1); pos = ngx_cpymem(pos, TENGINE_VER, sizeof(TENGINE_VER) - 1); } else { *pos++ = NGX_HTTP_V2_ENCODE_RAW | (sizeof("nginx") - 1); pos = ngx_cpymem(pos, "nginx", sizeof("nginx") - 1); } } if (r->headers_out.date == NULL) { *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX); *pos++ = (u_char) ngx_cached_http_time.len; pos = ngx_cpymem(pos, ngx_cached_http_time.data, ngx_cached_http_time.len); } if (r->headers_out.content_type.len) { *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX); if (r->headers_out.content_type_len == r->headers_out.content_type.len && r->headers_out.charset.len) { *pos = NGX_HTTP_V2_ENCODE_RAW; pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), r->headers_out.content_type.len + sizeof("; charset=") - 1 + r->headers_out.charset.len); p = pos; pos = ngx_cpymem(pos, r->headers_out.content_type.data, r->headers_out.content_type.len); pos = ngx_cpymem(pos, "; charset=", sizeof("; charset=") - 1); pos = ngx_cpymem(pos, r->headers_out.charset.data, r->headers_out.charset.len); /* update r->headers_out.content_type for possible logging */ r->headers_out.content_type.len = pos - p; r->headers_out.content_type.data = p; } else { *pos = NGX_HTTP_V2_ENCODE_RAW; pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), r->headers_out.content_type.len); pos = ngx_cpymem(pos, r->headers_out.content_type.data, r->headers_out.content_type.len); } } if (r->headers_out.content_length == NULL && r->headers_out.content_length_n >= 0) { *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX); p = pos; pos = ngx_sprintf(pos + 1, "%O", r->headers_out.content_length_n); *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1); } if (r->headers_out.last_modified == NULL && r->headers_out.last_modified_time != -1) { *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX); *pos++ = NGX_HTTP_V2_ENCODE_RAW | (sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1); pos = ngx_http_time(pos, r->headers_out.last_modified_time); } if (r->headers_out.location && r->headers_out.location->value.len) { *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX); *pos = NGX_HTTP_V2_ENCODE_RAW; pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), r->headers_out.location->value.len); pos = ngx_cpymem(pos, r->headers_out.location->value.data, r->headers_out.location->value.len); } #if (NGX_HTTP_GZIP) if (r->gzip_vary) { *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX); *pos++ = NGX_HTTP_V2_ENCODE_RAW | (sizeof("Accept-Encoding") - 1); pos = ngx_cpymem(pos, "Accept-Encoding", sizeof("Accept-Encoding") - 1); } #endif part = &r->headers_out.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 (header[i].hash == 0) { continue; } *pos++ = 0; *pos = NGX_HTTP_V2_ENCODE_RAW; pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), header[i].key.len); ngx_strlow(pos, header[i].key.data, header[i].key.len); pos += header[i].key.len; *pos = NGX_HTTP_V2_ENCODE_RAW; pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), header[i].value.len); pos = ngx_cpymem(pos, header[i].value.data, header[i].value.len); } frame = ngx_http_v2_create_headers_frame(r, start, pos); if (frame == NULL) { return NGX_ERROR; } ngx_http_v2_queue_blocked_frame(r->stream->connection, frame); cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { return NGX_ERROR; } cln->handler = ngx_http_v2_filter_cleanup; cln->data = r->stream; r->stream->queued = 1; fc->send_chain = ngx_http_v2_send_chain; fc->need_last_buf = 1; return ngx_http_v2_filter_send(fc, r->stream); }
void ngx_stream_init_connection(ngx_connection_t *c) { u_char text[NGX_SOCKADDR_STRLEN]; size_t len; ngx_int_t rc; ngx_uint_t i; struct sockaddr *sa; ngx_stream_port_t *port; struct sockaddr_in *sin; ngx_stream_in_addr_t *addr; ngx_stream_session_t *s; ngx_stream_addr_conf_t *addr_conf; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; ngx_stream_in6_addr_t *addr6; #endif ngx_stream_core_srv_conf_t *cscf; ngx_stream_core_main_conf_t *cmcf; /* find the server configuration for the address:port */ port = c->listening->servers; if (port->naddrs > 1) { /* * There are several addresses on this port and one of them * is the "*:port" wildcard so getsockname() is needed to determine * the server address. * * AcceptEx() already gave this address. */ if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { ngx_stream_close_connection(c); return; } sa = c->local_sockaddr; switch (sa->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) sa; addr6 = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { break; } } addr_conf = &addr6[i].conf; break; #endif default: /* AF_INET */ sin = (struct sockaddr_in *) sa; addr = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (addr[i].addr == sin->sin_addr.s_addr) { break; } } addr_conf = &addr[i].conf; break; } } else { switch (c->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: addr6 = port->addrs; addr_conf = &addr6[0].conf; break; #endif default: /* AF_INET */ addr = port->addrs; addr_conf = &addr[0].conf; break; } } s = ngx_pcalloc(c->pool, sizeof(ngx_stream_session_t)); if (s == NULL) { ngx_stream_close_connection(c); return; } s->signature = NGX_STREAM_MODULE; s->main_conf = addr_conf->ctx->main_conf; s->srv_conf = addr_conf->ctx->srv_conf; s->connection = c; c->data = s; cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); ngx_set_connection_log(c, cscf->error_log); len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1); ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%uA client %*s connected to %V", c->number, len, text, &addr_conf->addr_text); c->log->connection = c->number; c->log->handler = ngx_stream_log_error; c->log->data = s; c->log->action = "initializing connection"; c->log_error = NGX_ERROR_INFO; cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); if (cmcf->access_handler) { rc = cmcf->access_handler(s); if (rc != NGX_OK && rc != NGX_DECLINED) { ngx_stream_close_connection(c); return; } } #if (NGX_STREAM_SSL) { ngx_stream_ssl_conf_t *sslcf; sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module); if (addr_conf->ssl) { c->log->action = "SSL handshaking"; if (sslcf->ssl.ctx == NULL) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "no \"ssl_certificate\" is defined " "in server listening on SSL port"); ngx_stream_close_connection(c); return; } ngx_stream_ssl_init_connection(&sslcf->ssl, c); return; } } #endif ngx_stream_init_session(c); }
static void ngx_stream_proxy_init_upstream(ngx_stream_session_t *s) { int tcp_nodelay; u_char *p; ngx_connection_t *c, *pc; ngx_log_handler_pt handler; ngx_stream_upstream_t *u; ngx_stream_core_srv_conf_t *cscf; ngx_stream_proxy_srv_conf_t *pscf; u = s->upstream; pc = u->peer.connection; cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); if (pc->type == SOCK_STREAM && cscf->tcp_nodelay && pc->tcp_nodelay == NGX_TCP_NODELAY_UNSET) { ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, "tcp_nodelay"); tcp_nodelay = 1; if (setsockopt(pc->fd, IPPROTO_TCP, TCP_NODELAY, (const void *) &tcp_nodelay, sizeof(int)) == -1) { ngx_connection_error(pc, ngx_socket_errno, "setsockopt(TCP_NODELAY) failed"); ngx_stream_proxy_next_upstream(s); return; } pc->tcp_nodelay = NGX_TCP_NODELAY_SET; } if (u->proxy_protocol) { if (ngx_stream_proxy_send_proxy_protocol(s) != NGX_OK) { return; } u->proxy_protocol = 0; } pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); #if (NGX_STREAM_SSL) if (pc->type == SOCK_STREAM && pscf->ssl && pc->ssl == NULL) { ngx_stream_proxy_ssl_init_connection(s); return; } #endif c = s->connection; if (c->log->log_level >= NGX_LOG_INFO) { ngx_str_t str; u_char addr[NGX_SOCKADDR_STRLEN]; str.len = NGX_SOCKADDR_STRLEN; str.data = addr; if (ngx_connection_local_sockaddr(pc, &str, 1) == NGX_OK) { handler = c->log->handler; c->log->handler = NULL; ngx_log_error(NGX_LOG_INFO, c->log, 0, "%sproxy %V connected to %V", pc->type == SOCK_DGRAM ? "udp " : "", &str, u->peer.name); c->log->handler = handler; } } c->log->action = "proxying connection"; if (u->upstream_buf.start == NULL) { p = ngx_pnalloc(c->pool, pscf->buffer_size); if (p == NULL) { ngx_stream_proxy_finalize(s, NGX_ERROR); return; } u->upstream_buf.start = p; u->upstream_buf.end = p + pscf->buffer_size; u->upstream_buf.pos = p; u->upstream_buf.last = p; } if (c->type == SOCK_DGRAM) { s->received = c->buffer->last - c->buffer->pos; u->downstream_buf = *c->buffer; if (pscf->responses == 0) { pc->read->ready = 0; pc->read->eof = 1; } } u->connected = 1; pc->read->handler = ngx_stream_proxy_upstream_handler; pc->write->handler = ngx_stream_proxy_upstream_handler; if (pc->read->ready || pc->read->eof) { ngx_post_event(pc->read, &ngx_posted_events); } ngx_stream_proxy_process(s, 0, 1); }
// 在ngx_stream_optimize_servers里设置有连接发生时的回调函数 // 调用发生在ngx_event_accept.c:ngx_event_accept() // // 创建一个处理tcp的会话对象 // 要先检查限速和访问限制这两个功能模块 // 最后调用ngx_stream_init_session // 创建ctx数组,用于存储模块的ctx数据 // 调用handler,处理tcp数据,收发等等 void ngx_stream_init_connection(ngx_connection_t *c) { int tcp_nodelay; u_char text[NGX_SOCKADDR_STRLEN]; size_t len; ngx_int_t rc; ngx_uint_t i; struct sockaddr *sa; ngx_stream_port_t *port; struct sockaddr_in *sin; ngx_stream_in_addr_t *addr; ngx_stream_session_t *s; ngx_stream_addr_conf_t *addr_conf; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; ngx_stream_in6_addr_t *addr6; #endif ngx_stream_core_srv_conf_t *cscf; ngx_stream_core_main_conf_t *cmcf; /* find the server configuration for the address:port */ // 取监听同一端口的server信息 port = c->listening->servers; if (port->naddrs > 1) { /* * There are several addresses on this port and one of them * is the "*:port" wildcard so getsockname() is needed to determine * the server address. * * AcceptEx() already gave this address. */ if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { ngx_stream_close_connection(c); return; } sa = c->local_sockaddr; switch (sa->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) sa; addr6 = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { break; } } addr_conf = &addr6[i].conf; break; #endif default: /* AF_INET */ sin = (struct sockaddr_in *) sa; addr = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (addr[i].addr == sin->sin_addr.s_addr) { break; } } addr_conf = &addr[i].conf; break; } } else { // 唯一监听端口的server // addr_conf就是端口所在的server的配置数组 switch (c->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: addr6 = port->addrs; addr_conf = &addr6[0].conf; break; #endif default: /* AF_INET */ addr = port->addrs; addr_conf = &addr[0].conf; break; } } // 创建一个处理tcp的会话对象 s = ngx_pcalloc(c->pool, sizeof(ngx_stream_session_t)); if (s == NULL) { ngx_stream_close_connection(c); return; } // 设置会话对象的标志 s->signature = NGX_STREAM_MODULE; //设置会话正确的配置结构体 // addr_conf就是端口所在的server的配置数组 // 之后就可以用宏正确地获取模块的配置信息 s->main_conf = addr_conf->ctx->main_conf; s->srv_conf = addr_conf->ctx->srv_conf; // 设置会话关联的连接对象 s->connection = c; // 连接的data指针指向会话对象 c->data = s; // 获取相关的core配置 cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); ngx_set_connection_log(c, cscf->error_log); len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1); ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%uA client %*s connected to %V", c->number, len, text, &addr_conf->addr_text); // log的一些参数 c->log->connection = c->number; c->log->handler = ngx_stream_log_error; c->log->data = s; c->log->action = "initializing connection"; c->log_error = NGX_ERROR_INFO; // 一个stream{}块只能有一个main conf // 所以连接限速、访问限制的处理函数是相同的 // 但配置参数每个server可以不同 cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); // 是否有连接限速设置,在ngx_stream_limit_conn_module.c里设置 if (cmcf->limit_conn_handler) { rc = cmcf->limit_conn_handler(s); if (rc != NGX_DECLINED) { ngx_stream_close_connection(c); return; } } // 是否有访问限制 if (cmcf->access_handler) { rc = cmcf->access_handler(s); if (rc != NGX_OK && rc != NGX_DECLINED) { ngx_stream_close_connection(c); return; } } // 设置TCP_NODELAY,默认启用 if (cscf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) { ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, "tcp_nodelay"); tcp_nodelay = 1; 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_stream_close_connection(c); return; } c->tcp_nodelay = NGX_TCP_NODELAY_SET; } #if (NGX_STREAM_SSL) { ngx_stream_ssl_conf_t *sslcf; sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module); if (addr_conf->ssl) { c->log->action = "SSL handshaking"; if (sslcf->ssl.ctx == NULL) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "no \"ssl_certificate\" is defined " "in server listening on SSL port"); ngx_stream_close_connection(c); return; } ngx_stream_ssl_init_connection(&sslcf->ssl, c); return; } } #endif // 创建ctx数组,用于存储模块的ctx数据 // 调用handler,处理tcp数据,收发等等 ngx_stream_init_session(c); }
static ngx_int_t ngx_http_spdy_serverpush_header_filter(ngx_http_request_t *r) { int rc; size_t len; u_char *p, *buf, *last; ngx_buf_t *b; ngx_str_t host; ngx_uint_t i, j, count, port; ngx_chain_t *cl; ngx_list_part_t *part, *pt; ngx_table_elt_t *header, *h; ngx_connection_t *c; ngx_http_cleanup_t *cln; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; ngx_http_spdy_stream_t *stream; ngx_http_spdy_out_frame_t *frame; ngx_http_spdy_connection_t *sc; struct sockaddr_in *sin; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; #endif u_char addr[NGX_SOCKADDR_STRLEN]; if (!r->spdy_stream) { return ngx_http_next_header_filter(r); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "spdy serverpush module header filter"); if (r->header_sent) { return NGX_OK; } r->header_sent = 1; if (r != r->main) { return NGX_OK; } c = r->connection; if (r->method == NGX_HTTP_HEAD) { r->header_only = 1; } switch (r->headers_out.status) { case NGX_HTTP_OK: case NGX_HTTP_PARTIAL_CONTENT: break; case NGX_HTTP_NOT_MODIFIED: r->header_only = 1; break; case NGX_HTTP_NO_CONTENT: r->header_only = 1; ngx_str_null(&r->headers_out.content_type); r->headers_out.content_length = NULL; r->headers_out.content_length_n = -1; /* fall through */ default: r->headers_out.last_modified_time = -1; r->headers_out.last_modified = NULL; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "spdy serverpush module header filter 2"); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "From header Blaha spdy body filter \"%V?%V\"", &r->uri, &r->args); len = NGX_SPDY_NV_NUM_SIZE + ngx_http_spdy_nv_nsize("version") + ngx_http_spdy_nv_vsize("HTTP/1.1") + ngx_http_spdy_nv_nsize("status") + ngx_http_spdy_nv_vsize("418"); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); len += ngx_http_spdy_nv_nsize("url") +ngx_http_spdy_nv_vsize("https://localhost/test.js"); if (r->headers_out.server == NULL) { len += ngx_http_spdy_nv_nsize("server"); len += clcf->server_tokens ? ngx_http_spdy_nv_vsize(NGINX_VER) : ngx_http_spdy_nv_vsize("nginx"); } if (r->headers_out.date == NULL) { len += ngx_http_spdy_nv_nsize("date") + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT"); } if (r->headers_out.content_type.len) { len += ngx_http_spdy_nv_nsize("content-type") + NGX_SPDY_NV_VLEN_SIZE + r->headers_out.content_type.len; if (r->headers_out.content_type_len == r->headers_out.content_type.len && r->headers_out.charset.len) { len += sizeof("; charset=") - 1 + r->headers_out.charset.len; } } if (r->headers_out.content_length == NULL && r->headers_out.content_length_n >= 0) { len += ngx_http_spdy_nv_nsize("content-length") + NGX_SPDY_NV_VLEN_SIZE + NGX_OFF_T_LEN; } if (r->headers_out.last_modified == NULL && r->headers_out.last_modified_time != -1) { len += ngx_http_spdy_nv_nsize("last-modified") + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT"); } if (r->headers_out.location && r->headers_out.location->value.len && r->headers_out.location->value.data[0] == '/') { r->headers_out.location->hash = 0; if (clcf->server_name_in_redirect) { cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); host = cscf->server_name; } else if (r->headers_in.server.len) { host = r->headers_in.server; } else { host.len = NGX_SOCKADDR_STRLEN; host.data = addr; if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) { return NGX_ERROR; } } switch (c->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) c->local_sockaddr; port = ntohs(sin6->sin6_port); break; #endif #if (NGX_HAVE_UNIX_DOMAIN) case AF_UNIX: port = 0; break; #endif default: /* AF_INET */ sin = (struct sockaddr_in *) c->local_sockaddr; port = ntohs(sin->sin_port); break; } len += ngx_http_spdy_nv_nsize("location") + ngx_http_spdy_nv_vsize("https://") + host.len + r->headers_out.location->value.len; if (clcf->port_in_redirect) { #if (NGX_HTTP_SSL) if (c->ssl) port = (port == 443) ? 0 : port; else #endif port = (port == 80) ? 0 : port; } else { port = 0; } if (port) { len += sizeof(":65535") - 1; } } else { ngx_str_null(&host); port = 0; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "spdy serverpush module header filter 3"); part = &r->headers_out.headers.part; header = part->elts; int isSet=0; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; header = part->elts; i = 0; } if (header[i].hash == 0) { continue; } ngx_str_t xac = ngx_string("X-Associated-Content"); if(ngx_strncmp(&header[i].key, &xac, 20) == 0 ) { isSet=1; } len += NGX_SPDY_NV_NLEN_SIZE + header[i].key.len + NGX_SPDY_NV_VLEN_SIZE + header[i].value.len; } buf = ngx_alloc(len, r->pool->log); if (buf == NULL) { return NGX_ERROR; } last = buf + NGX_SPDY_NV_NUM_SIZE; last = ngx_http_spdy_nv_write_name(last, "version"); last = ngx_http_spdy_nv_write_val(last, "HTTP/1.1"); last = ngx_http_spdy_nv_write_name(last, "status"); last = ngx_spdy_frame_write_uint16(last, 3); last = ngx_sprintf(last, "%03ui", r->headers_out.status); count = 2; last = ngx_http_spdy_nv_write_name(last, "url"); last = ngx_http_spdy_nv_write_val(last, "https://localhost/test.js"); count++; if (r->headers_out.server == NULL) { last = ngx_http_spdy_nv_write_name(last, "server"); last = clcf->server_tokens ? ngx_http_spdy_nv_write_val(last, NGINX_VER) : ngx_http_spdy_nv_write_val(last, "nginx"); count++; } if (r->headers_out.date == NULL) { last = ngx_http_spdy_nv_write_name(last, "date"); last = ngx_http_spdy_nv_write_vlen(last, ngx_cached_http_time.len); last = ngx_cpymem(last, ngx_cached_http_time.data, ngx_cached_http_time.len); count++; } if (r->headers_out.content_type.len) { last = ngx_http_spdy_nv_write_name(last, "content-type"); p = last + NGX_SPDY_NV_VLEN_SIZE; last = ngx_cpymem(p, r->headers_out.content_type.data, r->headers_out.content_type.len); if (r->headers_out.content_type_len == r->headers_out.content_type.len && r->headers_out.charset.len) { last = ngx_cpymem(last, "; charset=", sizeof("; charset=") - 1); last = ngx_cpymem(last, r->headers_out.charset.data, r->headers_out.charset.len); /* update r->headers_out.content_type for possible logging */ r->headers_out.content_type.len = last - p; r->headers_out.content_type.data = p; } (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, r->headers_out.content_type.len); count++; } if (r->headers_out.content_length == NULL && r->headers_out.content_length_n >= 0) { last = ngx_http_spdy_nv_write_name(last, "content-length"); p = last + NGX_SPDY_NV_VLEN_SIZE; last = ngx_sprintf(p, "%O", r->headers_out.content_length_n); (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, last - p); count++; } if (r->headers_out.last_modified == NULL && r->headers_out.last_modified_time != -1) { last = ngx_http_spdy_nv_write_name(last, "last-modified"); p = last + NGX_SPDY_NV_VLEN_SIZE; last = ngx_http_time(p, r->headers_out.last_modified_time); (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, last - p); count++; } if (host.data) { last = ngx_http_spdy_nv_write_name(last, "location"); p = last + NGX_SPDY_NV_VLEN_SIZE; last = ngx_cpymem(p, "http", sizeof("http") - 1); #if (NGX_HTTP_SSL) if (c->ssl) { *last++ ='s'; } #endif *last++ = ':'; *last++ = '/'; *last++ = '/'; last = ngx_cpymem(last, host.data, host.len); if (port) { last = ngx_sprintf(last, ":%ui", port); } last = ngx_cpymem(last, r->headers_out.location->value.data, r->headers_out.location->value.len); /* update r->headers_out.location->value for possible logging */ r->headers_out.location->value.len = last - p; r->headers_out.location->value.data = p; ngx_str_set(&r->headers_out.location->key, "location"); (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, r->headers_out.location->value.len); count++; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "spdy serverpush module header filter 4"); part = &r->headers_out.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 (header[i].hash == 0 || header[i].hash == 2) { continue; } if ((header[i].key.len == 6 && ngx_strncasecmp(header[i].key.data, (u_char *) "status", 6) == 0) || (header[i].key.len == 7 && ngx_strncasecmp(header[i].key.data, (u_char *) "version", 7) == 0)) { header[i].hash = 0; continue; } last = ngx_http_spdy_nv_write_nlen(last, header[i].key.len); ngx_strlow(last, header[i].key.data, header[i].key.len); last += header[i].key.len; p = last + NGX_SPDY_NV_VLEN_SIZE; last = ngx_cpymem(p, header[i].value.data, header[i].value.len); pt = part; h = header; for (j = i + 1; /* void */; j++) { if (j >= pt->nelts) { if (pt->next == NULL) { break; } pt = pt->next; h = pt->elts; j = 0; } if (h[j].hash == 0 || h[j].hash == 2 || h[j].key.len != header[i].key.len || ngx_strncasecmp(header[i].key.data, h[j].key.data, header[i].key.len)) { continue; } *last++ = '\0'; last = ngx_cpymem(last, h[j].value.data, h[j].value.len); h[j].hash = 2; } (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, last - p); count++; } (void) ngx_spdy_frame_write_uint16(buf, count); stream = r->spdy_stream; sc = stream->connection; if(isSet) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "spdy serverpush module header filter 5"); //ngx_http_spdy_stream_t *myStream; myStream = ngx_http_spdy_create_stream(sc, get_next_even_stream_id(), 0); len = last - buf; b = ngx_create_temp_buf(r->pool, NGX_SPDY_FRAME_HEADER_SIZE + NGX_SPDY_SYN_STREAM_SIZE + deflateBound(&sc->zstream_out, len)); if (b == NULL) { ngx_free(buf); return NGX_ERROR; } b->last += NGX_SPDY_FRAME_HEADER_SIZE + NGX_SPDY_SYN_STREAM_SIZE; sc->zstream_out.next_in = buf; sc->zstream_out.avail_in = len; sc->zstream_out.next_out = b->last; sc->zstream_out.avail_out = b->end - b->last; rc = deflate(&sc->zstream_out, Z_SYNC_FLUSH); ngx_free(buf); if (rc != Z_OK) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "spdy deflate() failed: %d", rc); return NGX_ERROR; } ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", sc->zstream_out.next_in, sc->zstream_out.next_out, sc->zstream_out.avail_in, sc->zstream_out.avail_out, rc); b->last = sc->zstream_out.next_out; p = b->pos; p = ngx_spdy_frame_write_head(p, NGX_SPDY_SYN_STREAM); len = b->last - b->pos; r->header_size = len; if (r->header_only) { b->last_buf = 1; p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_FIN, len - NGX_SPDY_FRAME_HEADER_SIZE); } else { p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_UNIDIRECTIONAL, len - NGX_SPDY_FRAME_HEADER_SIZE); } p= ngx_spdy_frame_write_sid(p, myStream->id); (void) ngx_spdy_frame_write_associated_sid(p, stream->id); cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } cl->buf = b; cl->next = NULL; frame = ngx_palloc(r->pool, sizeof(ngx_http_spdy_out_frame_t)); if (frame == NULL) { return NGX_ERROR; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "spdy serverpush module header filter 6"); frame->first = cl; frame->last = cl; frame->handler = ngx_http_spdy_syn_frame_handler; frame->free = NULL; frame->stream = myStream; frame->size = len; frame->priority = myStream->priority; frame->blocked = 1; frame->fin = r->header_only; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0, "spdy:%ui create JOLLY SYN_STREAM frame %p: size:%uz", myStream->id, frame, frame->size); ngx_http_spdy_queue_blocked_frame(sc, frame); r->blocked++; cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { return NGX_ERROR; } cln->handler = ngx_http_spdy_filter_cleanup; cln->data = myStream; myStream->waiting = 1; ngx_http_spdy_serverpush_filter_send(c, myStream); return ngx_http_next_header_filter(r); } else { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "spdy serverpush module header filter 7"); return NGX_OK; } }
static void ngx_stream_proxy_init_upstream(ngx_stream_session_t *s) { u_char *p; ngx_connection_t *c, *pc; ngx_log_handler_pt handler; ngx_stream_upstream_t *u; ngx_stream_proxy_srv_conf_t *pscf; u = s->upstream; if (u->proxy_protocol) { if (ngx_stream_proxy_send_proxy_protocol(s) != NGX_OK) { return; } u->proxy_protocol = 0; } pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); pc = u->peer.connection; #if (NGX_STREAM_SSL) if (pscf->ssl && pc->ssl == NULL) { ngx_stream_proxy_ssl_init_connection(s); return; } #endif c = s->connection; if (c->log->log_level >= NGX_LOG_INFO) { ngx_str_t s; u_char addr[NGX_SOCKADDR_STRLEN]; s.len = NGX_SOCKADDR_STRLEN; s.data = addr; if (ngx_connection_local_sockaddr(pc, &s, 1) == NGX_OK) { handler = c->log->handler; c->log->handler = NULL; ngx_log_error(NGX_LOG_INFO, c->log, 0, "proxy %V connected to %V", &s, u->peer.name); c->log->handler = handler; } } c->log->action = "proxying connection"; p = ngx_pnalloc(c->pool, pscf->buffer_size); if (p == NULL) { ngx_stream_proxy_finalize(s, NGX_ERROR); return; } u->upstream_buf.start = p; u->upstream_buf.end = p + pscf->buffer_size; u->upstream_buf.pos = p; u->upstream_buf.last = p; u->connected = 1; pc->read->handler = ngx_stream_proxy_upstream_handler; pc->write->handler = ngx_stream_proxy_upstream_handler; if (ngx_stream_proxy_process(s, 1, 0) != NGX_OK) { return; } ngx_stream_proxy_process(s, 0, 1); }
static ngx_int_t ngx_http_header_filter(ngx_http_request_t *r) { u_char *p; size_t len; ngx_str_t host, *status_line; ngx_buf_t *b; ngx_uint_t status, i, port; ngx_chain_t out; ngx_list_part_t *part; ngx_table_elt_t *header; ngx_connection_t *c; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; struct sockaddr_in *sin; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; #endif u_char addr[NGX_SOCKADDR_STRLEN]; if (r->header_sent) { return NGX_OK; } r->header_sent = 1; if (r != r->main) { return NGX_OK; } if (r->http_version < NGX_HTTP_VERSION_10) { return NGX_OK; } if (r->method == NGX_HTTP_HEAD) { r->header_only = 1; } if (r->headers_out.last_modified_time != -1) { if (r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT && r->headers_out.status != NGX_HTTP_NOT_MODIFIED) { r->headers_out.last_modified_time = -1; r->headers_out.last_modified = NULL; } } len = sizeof("HTTP/1.x ") - 1 + sizeof(CRLF) - 1 /* the end of the header */ + sizeof(CRLF) - 1; /* status line */ if (r->headers_out.status_line.len) { len += r->headers_out.status_line.len; status_line = &r->headers_out.status_line; #if (NGX_SUPPRESS_WARN) status = 0; #endif } else { status = r->headers_out.status; if (status >= NGX_HTTP_OK && status < NGX_HTTP_LAST_2XX) { /* 2XX */ if (status == NGX_HTTP_NO_CONTENT) { r->header_only = 1; ngx_str_null(&r->headers_out.content_type); r->headers_out.last_modified_time = -1; r->headers_out.last_modified = NULL; r->headers_out.content_length = NULL; r->headers_out.content_length_n = -1; } status -= NGX_HTTP_OK; status_line = &ngx_http_status_lines[status]; len += ngx_http_status_lines[status].len; } else if (status >= NGX_HTTP_MOVED_PERMANENTLY && status < NGX_HTTP_LAST_3XX) { /* 3XX */ if (status == NGX_HTTP_NOT_MODIFIED) { r->header_only = 1; } status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX; status_line = &ngx_http_status_lines[status]; len += ngx_http_status_lines[status].len; } else if (status >= NGX_HTTP_BAD_REQUEST && status < NGX_HTTP_LAST_4XX) { /* 4XX */ status = status - NGX_HTTP_BAD_REQUEST + NGX_HTTP_OFF_4XX; status_line = &ngx_http_status_lines[status]; len += ngx_http_status_lines[status].len; } else if (status >= NGX_HTTP_INTERNAL_SERVER_ERROR && status < NGX_HTTP_LAST_5XX) { /* 5XX */ status = status - NGX_HTTP_INTERNAL_SERVER_ERROR + NGX_HTTP_OFF_5XX; status_line = &ngx_http_status_lines[status]; len += ngx_http_status_lines[status].len; } else { len += NGX_INT_T_LEN + 1 /* SP */; status_line = NULL; } if (status_line && status_line->len == 0) { status = r->headers_out.status; len += NGX_INT_T_LEN + 1 /* SP */; status_line = NULL; } } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (r->headers_out.server == NULL) { len += clcf->server_tokens ? sizeof(ngx_http_server_full_string) - 1: sizeof(ngx_http_server_string) - 1; } if (r->headers_out.date == NULL) { len += sizeof("Date: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1; } if (r->headers_out.content_type.len) { len += sizeof("Content-Type: ") - 1 + r->headers_out.content_type.len + 2; if (r->headers_out.content_type_len == r->headers_out.content_type.len && r->headers_out.charset.len) { len += sizeof("; charset=") - 1 + r->headers_out.charset.len; } } if (r->headers_out.content_length == NULL && r->headers_out.content_length_n >= 0) { len += sizeof("Content-Length: ") - 1 + NGX_OFF_T_LEN + 2; } if (r->headers_out.last_modified == NULL && r->headers_out.last_modified_time != -1) { len += sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1; } c = r->connection; if (r->headers_out.location && r->headers_out.location->value.len && r->headers_out.location->value.data[0] == '/') { r->headers_out.location->hash = 0; if (clcf->server_name_in_redirect) { cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); host = cscf->server_name; } else if (r->headers_in.server.len) { host = r->headers_in.server; } else { host.len = NGX_SOCKADDR_STRLEN; host.data = addr; if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) { return NGX_ERROR; } } switch (c->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) c->local_sockaddr; port = ntohs(sin6->sin6_port); break; #endif #if (NGX_HAVE_UNIX_DOMAIN) case AF_UNIX: port = 0; break; #endif default: /* AF_INET */ sin = (struct sockaddr_in *) c->local_sockaddr; port = ntohs(sin->sin_port); break; } len += sizeof("Location: https://") - 1 + host.len + r->headers_out.location->value.len + 2; if (clcf->port_in_redirect) { #if (NGX_HTTP_SSL) if (c->ssl) port = (port == 443) ? 0 : port; else #endif port = (port == 80) ? 0 : port; } else { port = 0; } if (port) { len += sizeof(":65535") - 1; } } else { ngx_str_null(&host); port = 0; } if (r->chunked) { len += sizeof("Transfer-Encoding: chunked" CRLF) - 1; } if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) { len += sizeof("Connection: upgrade" CRLF) - 1; } else if (r->keepalive) { len += sizeof("Connection: keep-alive" CRLF) - 1; /* * MSIE and Opera ignore the "Keep-Alive: timeout=<N>" header. * MSIE keeps the connection alive for about 60-65 seconds. * Opera keeps the connection alive very long. * Mozilla keeps the connection alive for N plus about 1-10 seconds. * Konqueror keeps the connection alive for about N seconds. */ if (clcf->keepalive_header) { len += sizeof("Keep-Alive: timeout=") - 1 + NGX_TIME_T_LEN + 2; } } else { len += sizeof("Connection: close" CRLF) - 1; } #if (NGX_HTTP_GZIP) if (r->gzip_vary) { if (clcf->gzip_vary) { len += sizeof("Vary: Accept-Encoding" CRLF) - 1; } else { r->gzip_vary = 0; } } #endif part = &r->headers_out.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 (header[i].hash == 0) { continue; } len += header[i].key.len + sizeof(": ") - 1 + header[i].value.len + sizeof(CRLF) - 1; } b = ngx_create_temp_buf(r->pool, len); if (b == NULL) { return NGX_ERROR; } /* "HTTP/1.x " */ b->last = ngx_cpymem(b->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1); /* status line */ if (status_line) { b->last = ngx_copy(b->last, status_line->data, status_line->len); } else { b->last = ngx_sprintf(b->last, "%03ui ", status); } *b->last++ = CR; *b->last++ = LF; if (r->headers_out.server == NULL) { if (clcf->server_tokens) { p = (u_char *) ngx_http_server_full_string; len = sizeof(ngx_http_server_full_string) - 1; } else { p = (u_char *) ngx_http_server_string; len = sizeof(ngx_http_server_string) - 1; } b->last = ngx_cpymem(b->last, p, len); } if (r->headers_out.date == NULL) { b->last = ngx_cpymem(b->last, "Date: ", sizeof("Date: ") - 1); b->last = ngx_cpymem(b->last, ngx_cached_http_time.data, ngx_cached_http_time.len); *b->last++ = CR; *b->last++ = LF; } if (r->headers_out.content_type.len) { b->last = ngx_cpymem(b->last, "Content-Type: ", sizeof("Content-Type: ") - 1); p = b->last; b->last = ngx_copy(b->last, r->headers_out.content_type.data, r->headers_out.content_type.len); if (r->headers_out.content_type_len == r->headers_out.content_type.len && r->headers_out.charset.len) { b->last = ngx_cpymem(b->last, "; charset=", sizeof("; charset=") - 1); b->last = ngx_copy(b->last, r->headers_out.charset.data, r->headers_out.charset.len); /* update r->headers_out.content_type for possible logging */ r->headers_out.content_type.len = b->last - p; r->headers_out.content_type.data = p; } *b->last++ = CR; *b->last++ = LF; } if (r->headers_out.content_length == NULL && r->headers_out.content_length_n >= 0) { b->last = ngx_sprintf(b->last, "Content-Length: %O" CRLF, r->headers_out.content_length_n); } if (r->headers_out.last_modified == NULL && r->headers_out.last_modified_time != -1) { b->last = ngx_cpymem(b->last, "Last-Modified: ", sizeof("Last-Modified: ") - 1); b->last = ngx_http_time(b->last, r->headers_out.last_modified_time); *b->last++ = CR; *b->last++ = LF; } if (host.data) { p = b->last + sizeof("Location: ") - 1; b->last = ngx_cpymem(b->last, "Location: http", sizeof("Location: http") - 1); #if (NGX_HTTP_SSL) if (c->ssl) { *b->last++ ='s'; } #endif *b->last++ = ':'; *b->last++ = '/'; *b->last++ = '/'; b->last = ngx_copy(b->last, host.data, host.len); if (port) { b->last = ngx_sprintf(b->last, ":%ui", port); } b->last = ngx_copy(b->last, r->headers_out.location->value.data, r->headers_out.location->value.len); /* update r->headers_out.location->value for possible logging */ r->headers_out.location->value.len = b->last - p; r->headers_out.location->value.data = p; ngx_str_set(&r->headers_out.location->key, "Location"); *b->last++ = CR; *b->last++ = LF; } if (r->chunked) { b->last = ngx_cpymem(b->last, "Transfer-Encoding: chunked" CRLF, sizeof("Transfer-Encoding: chunked" CRLF) - 1); } if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) { b->last = ngx_cpymem(b->last, "Connection: upgrade" CRLF, sizeof("Connection: upgrade" CRLF) - 1); } else if (r->keepalive) { b->last = ngx_cpymem(b->last, "Connection: keep-alive" CRLF, sizeof("Connection: keep-alive" CRLF) - 1); if (clcf->keepalive_header) { b->last = ngx_sprintf(b->last, "Keep-Alive: timeout=%T" CRLF, clcf->keepalive_header); } } else { b->last = ngx_cpymem(b->last, "Connection: close" CRLF, sizeof("Connection: close" CRLF) - 1); } #if (NGX_HTTP_GZIP) if (r->gzip_vary) { b->last = ngx_cpymem(b->last, "Vary: Accept-Encoding" CRLF, sizeof("Vary: Accept-Encoding" CRLF) - 1); } #endif part = &r->headers_out.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 (header[i].hash == 0) { continue; } b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len); *b->last++ = ':'; *b->last++ = ' '; b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); *b->last++ = CR; *b->last++ = LF; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "%*s", (size_t) (b->last - b->pos), b->pos); /* the end of HTTP header */ *b->last++ = CR; *b->last++ = LF; r->header_size = b->last - b->pos; if (r->header_only) { b->last_buf = 1; } out.buf = b; out.next = NULL; return ngx_http_write_filter(r, &out); }
static void ngx_tcp_init_session(ngx_event_t *rev) { ngx_time_t *tp; ngx_uint_t i; ngx_connection_t *c; struct sockaddr_in *sin; ngx_tcp_port_t *port; ngx_tcp_in_addr_t *addr; ngx_tcp_log_ctx_t *ctx; ngx_tcp_addr_conf_t *addr_conf; ngx_tcp_session_t *s; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; ngx_tcp_in6_addr_t *addr6; #endif ngx_tcp_core_srv_conf_t *cscf; /* #if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); #endif */ c = rev->data; s = c->data; if (s == NULL) { s = ngx_pcalloc(c->pool, sizeof(ngx_tcp_session_t)); if (s == NULL) { ngx_tcp_close_connection(c); return; } c->data = s; } ctx = c->log->data; ctx->session= s; ctx->client= &c->addr_text; c->requests++; s->connection = c; s->signature = NGX_TCP_MODULE; /* find the server configuration for the address:port */ port = c->listening->servers; if (port->naddrs > 1) { /* * there are several addresses on this port and one of them * is an "*:port" wildcard so getsockname() in ngx_tcp_server_addr() * is required to determine a server address */ if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { ngx_tcp_close_connection(c); return; } switch (c->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) c->local_sockaddr; addr6 = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { break; } } addr_conf = &addr6[i].conf; break; #endif default: /* AF_INET */ sin = (struct sockaddr_in *) c->local_sockaddr; addr = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (addr[i].addr == sin->sin_addr.s_addr) { break; } } addr_conf = &addr[i].conf; break; } } else { switch (c->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: addr6 = port->addrs; addr_conf = &addr6[0].conf; break; #endif default: /* AF_INET */ addr = port->addrs; addr_conf = &addr[0].conf; break; } } if (addr_conf->default_ctx) { s->main_conf = addr_conf->default_ctx->main_conf; s->srv_conf = addr_conf->default_ctx->srv_conf; } else { s->main_conf = addr_conf->ctx->main_conf; s->srv_conf = addr_conf->ctx->srv_conf; } s->addr_text = &addr_conf->addr_text; if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); ngx_tcp_close_connection(c); return; } rev->handler = ngx_tcp_process_session; //s->read_event_handler = ngx_tcp_block_reading; /* #if (NGX_HTTP_SSL) { ngx_tcp_ssl_srv_conf_t *sscf; sscf = ngx_tcp_get_module_srv_conf(r, ngx_tcp_ssl_module); if (sscf->enable || addr_conf->ssl) { if (c->ssl == NULL) { c->log->action = "SSL handshaking"; if (addr_conf->ssl && sscf->ssl.ctx == NULL) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "no \"ssl_certificate\" is defined " "in server listening on SSL port"); ngx_tcp_close_connection(c); return; } if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER) != NGX_OK) { ngx_tcp_close_connection(c); return; } rev->handler = ngx_tcp_ssl_handshake; } r->main_filter_need_in_memory = 1; } } #endif */ s->pool = c->pool; cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module); if (cscf == NULL) { ngx_tcp_finalize_session(s); return; } s->ctx = ngx_pcalloc(s->pool, sizeof(void *) * ngx_tcp_max_module); if (s->ctx == NULL) { ngx_tcp_finalize_session(s); return; } tp = ngx_timeofday(); s->start_sec = tp->sec; s->start_msec = tp->msec; s->bytes_read = 0; s->bytes_write = 0; ngx_tcp_set_session_socket(s); /* #if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); r->stat_reading = 1; (void) ngx_atomic_fetch_add(ngx_stat_requests, 1); #endif */ rev->handler(rev); }
SV *ngx_http_psgi_create_env(pTHX_ ngx_http_request_t *r, char *app) { ngx_list_part_t *part; ngx_table_elt_t *h; ngx_uint_t i, c, x; SV *_version[2]; AV *version; HV* env = newHV(); /* PSGI version 1.0, arrayref [1,0] */ _version[0] = newSViv(1); _version[1] = newSViv(0); version = av_make(2, _version); SvREFCNT_dec(_version[0]); SvREFCNT_dec(_version[1]); hv_store(env, "psgi.version", sizeof("psgi.version")-1, newRV_noinc((SV*)version), 0); /* FIXME: after any of this two operations $! is set to 'Inappropriate ioctl for device' */ SV *errors_h = PerlIONginxError_newhandle(aTHX_ r); if (errors_h == NULL) return NULL; hv_store(env, "psgi.errors", sizeof("psgi.errors")-1, errors_h, 0); SV *input_h = PerlIONginxInput_newhandle(aTHX_ r); if (input_h == NULL) return NULL; hv_store(env, "psgi.input", sizeof("psgi.input")-1, input_h, 0); /* Detect scheme. * TODO: Check if only http and https schemes allowed here. What about ws and others? * FIXME: mb nginx should parse scheme in safe way: [a-z][a-z0-9\=\0\.]* allowed to be valid scheme (rfc3986) * but nginx allows only [a-z]+ */ #if (NGX_HTTP_SSL) char *scheme; if (r->connection->ssl) { scheme = "https"; } else { scheme = "http"; } hv_store(env, "psgi.url_scheme", sizeof("psgi.url_scheme")-1, newSVpv(scheme, 0), 0); #else hv_store(env, "psgi.url_scheme", sizeof("psgi.url_scheme")-1, newSVpv("http", 0), 0); #endif // Buffered body in file if (r->request_body != NULL && r->request_body->temp_file != NULL) { hv_store(env, "psgix.input.buffered", sizeof("psgix.input.buffered")-1, newSViv(1), 0); } /* port defined in first line of HTTP request and parsed by nginx */ if (r->port_start) { STRLEN port_len = r->port_end - r->port_start; hv_store(env, "SERVER_PORT", sizeof("SERVER_PORT")-1, newSVpv((char *)r->port_start, port_len), 0); } else { /* copypasted from ngx_http_variables.c: get port from nginx conf */ /* TODO: Maybe reuse code from ngx_http_variables.c is a good idea? */ ngx_uint_t port; struct sockaddr_in *sin; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; #endif u_char *strport; if (ngx_connection_local_sockaddr(r->connection, NULL, 0) != NGX_OK) { // TODO: Throw error return NULL; } strport = ngx_pnalloc(r->pool, sizeof("65535") - 1); if (strport == NULL) { return NULL; } switch (r->connection->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) r->connection->local_sockaddr; port = ntohs(sin6->sin6_port); break; #endif default: /* AF_INET */ sin = (struct sockaddr_in *) r->connection->local_sockaddr; port = ntohs(sin->sin_port); break; } if (port > 0 && port < 65536) { hv_store(env, "SERVER_PORT", sizeof("SERVER_PORT")-1, newSVuv(port), 0); } else { hv_store(env, "SERVER_PORT", sizeof("SERVER_PORT")-1, newSVpv("", 0), 0); } } hv_store(env, "SERVER_PROTOCOL", sizeof("SERVER_PROTOCOL")-1, newSVpv((char *)r->http_protocol.data, r->http_protocol.len), 0); if (r->headers_in.content_length_n != -1) { hv_store(env, "CONTENT_LENGTH", sizeof("CONTENT_LENGTH")-1, newSViv(r->headers_in.content_length_n), 0); } if (r->headers_in.content_type != NULL) { hv_store(env, "CONTENT_TYPE", sizeof("CONTENT_TYPE")-1, newSVpv((char*)r->headers_in.content_type->value.data, r->headers_in.content_type->value.len), 0); } hv_store(env, "REQUEST_URI", sizeof("REQUEST_URI")-1, newSVpv((char *)r->unparsed_uri.data, r->unparsed_uri.len), 0); /* TODO: SCRIPT_NAME should be string matched by 'location' value in nginx.conf */ hv_store(env, "SCRIPT_NAME", sizeof("SCRIPT_NAME")-1, newSVpv("", 0), 0); /* FIXME: * PATH_INFO should be relative to SCRIPT_NAME (current 'location') path in nginx.conf * How to achieve this? Should I allow psgi only in 'exact match' locations? * It would be hard to find PATH_INFO for locations like "location ~ /(foo|bar)/.* { }". Or it wouldn't? */ hv_store(env, "PATH_INFO", sizeof("PATH_INFO")-1, newSVpv((char *)r->uri.data, r->uri.len), 0); hv_store(env, "REQUEST_METHOD", sizeof("REQUEST_METHOD")-1, newSVpv((char *)r->method_name.data, r->method_name.len), 0); if (r->args.len > 0) { hv_store(env, "QUERY_STRING", sizeof("QUERY_STRING")-1, newSVpv((char *)r->args.data, r->args.len), 0); } else { hv_store(env, "QUERY_STRING", sizeof("QUERY_STRING")-1, newSVpv("", 0), 0); } if (r->host_start && r->host_end) { hv_store(env, "SERVER_NAME", sizeof("SERVER_NAME")-1, newSVpv((char *)r->host_start, r->host_end - r->host_start), 0); } else { ngx_http_core_srv_conf_t *cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); hv_store(env, "SERVER_NAME", sizeof("SERVER_NAME")-1, newSVpv((char *)cscf->server_name.data, cscf->server_name.len), 0); } hv_store(env, "REMOTE_ADDR", sizeof("REMOTE_ADDR")-1, newSVpv((char *)r->connection->addr_text.data, r->connection->addr_text.len), 0); /* TODO * * psgi.multithread * psgi.multiprocess */ part = &r->headers_in.headers.part; h = part->elts; c = 0; for (i = 0; /* void */ ; i++) { ngx_str_t name; u_char *p; if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } /* The environment MUST NOT contain keys named HTTP_CONTENT_TYPE or HTTP_CONTENT_LENGTH. * PSGI 1.09_3 */ if (ngx_strncasecmp(h[i].key.data, (u_char*)"CONTENT-LENGTH", h[i].key.len) == 0) { continue; } if (ngx_strncasecmp(h[i].key.data, (u_char*)"CONTENT-TYPE", h[i].key.len) == 0) { continue; } p = ngx_pnalloc(r->pool, sizeof("HTTP_") - 1 + h[i].key.len); if (p == NULL) { return NULL; } name.data = p; name.len = sizeof("HTTP_") + h[i].key.len -1 ; p = ngx_copy(p, (u_char*)"HTTP_", sizeof("HTTP_")-1); p = ngx_copy(p, h[i].key.data, h[i].key.len ); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "Set env header: '%s' => '%s'", h[i].key.data, h[i].value.data); x = h[i].key.len + sizeof("HTTP_"); while (x > 0) { if (name.data[x] == '-') { name.data[x] = '_'; } else { name.data[x] = ngx_toupper(name.data[x]); } x--; } SV **exists = hv_fetch(env, (char*)name.data, name.len, 0); if (exists == NULL) { hv_store(env, (char *)name.data, name.len, newSVpv((char *)h[i].value.data, h[i].value.len), 0); } else { /* join ',', @values; * FIXME: Can I do this better */ SV *newval = newSVpvf("%s,%s", SvPV_nolen(*exists), h[i].value.data); hv_store(env, (char *)name.data, name.len, newval, 0); } c += 2; } return newRV_noinc((SV*)env); }