mirror of
https://github.com/nginx/nginx.git
synced 2024-11-24 04:49:01 +08:00
Stream: ngx_stream_pass_module.
The module allows to pass connections from Stream to other modules such as HTTP or Mail, as well as back to Stream. Previously, this was only possible with proxying. Connections with preread buffer read out from socket cannot be passed. The module allows selective SSL termination based on SNI. stream { server { listen 8000 default_server; ssl_preread on; ... } server { listen 8000; server_name foo.example.com; pass 127.0.0.1:8001; # to HTTP } server { listen 8000; server_name bar.example.com; ... } } http { server { listen 8001 ssl; ... location / { root html; } } }
This commit is contained in:
parent
d21675228a
commit
a168b810e2
10
auto/modules
10
auto/modules
@ -1166,6 +1166,16 @@ if [ $STREAM != NO ]; then
|
|||||||
. auto/module
|
. auto/module
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ $STREAM_PASS = YES ]; then
|
||||||
|
ngx_module_name=ngx_stream_pass_module
|
||||||
|
ngx_module_deps=
|
||||||
|
ngx_module_srcs=src/stream/ngx_stream_pass_module.c
|
||||||
|
ngx_module_libs=
|
||||||
|
ngx_module_link=$STREAM_PASS
|
||||||
|
|
||||||
|
. auto/module
|
||||||
|
fi
|
||||||
|
|
||||||
if [ $STREAM_SET = YES ]; then
|
if [ $STREAM_SET = YES ]; then
|
||||||
ngx_module_name=ngx_stream_set_module
|
ngx_module_name=ngx_stream_set_module
|
||||||
ngx_module_deps=
|
ngx_module_deps=
|
||||||
|
@ -127,6 +127,7 @@ STREAM_GEOIP=NO
|
|||||||
STREAM_MAP=YES
|
STREAM_MAP=YES
|
||||||
STREAM_SPLIT_CLIENTS=YES
|
STREAM_SPLIT_CLIENTS=YES
|
||||||
STREAM_RETURN=YES
|
STREAM_RETURN=YES
|
||||||
|
STREAM_PASS=YES
|
||||||
STREAM_SET=YES
|
STREAM_SET=YES
|
||||||
STREAM_UPSTREAM_HASH=YES
|
STREAM_UPSTREAM_HASH=YES
|
||||||
STREAM_UPSTREAM_LEAST_CONN=YES
|
STREAM_UPSTREAM_LEAST_CONN=YES
|
||||||
@ -337,6 +338,7 @@ use the \"--with-mail_ssl_module\" option instead"
|
|||||||
--without-stream_split_clients_module)
|
--without-stream_split_clients_module)
|
||||||
STREAM_SPLIT_CLIENTS=NO ;;
|
STREAM_SPLIT_CLIENTS=NO ;;
|
||||||
--without-stream_return_module) STREAM_RETURN=NO ;;
|
--without-stream_return_module) STREAM_RETURN=NO ;;
|
||||||
|
--without-stream_pass_module) STREAM_PASS=NO ;;
|
||||||
--without-stream_set_module) STREAM_SET=NO ;;
|
--without-stream_set_module) STREAM_SET=NO ;;
|
||||||
--without-stream_upstream_hash_module)
|
--without-stream_upstream_hash_module)
|
||||||
STREAM_UPSTREAM_HASH=NO ;;
|
STREAM_UPSTREAM_HASH=NO ;;
|
||||||
@ -556,6 +558,7 @@ cat << END
|
|||||||
--without-stream_split_clients_module
|
--without-stream_split_clients_module
|
||||||
disable ngx_stream_split_clients_module
|
disable ngx_stream_split_clients_module
|
||||||
--without-stream_return_module disable ngx_stream_return_module
|
--without-stream_return_module disable ngx_stream_return_module
|
||||||
|
--without-stream_pass_module disable ngx_stream_pass_module
|
||||||
--without-stream_set_module disable ngx_stream_set_module
|
--without-stream_set_module disable ngx_stream_set_module
|
||||||
--without-stream_upstream_hash_module
|
--without-stream_upstream_hash_module
|
||||||
disable ngx_stream_upstream_hash_module
|
disable ngx_stream_upstream_hash_module
|
||||||
|
276
src/stream/ngx_stream_pass_module.c
Normal file
276
src/stream/ngx_stream_pass_module.c
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Roman Arutyunyan
|
||||||
|
* Copyright (C) Nginx, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <ngx_config.h>
|
||||||
|
#include <ngx_core.h>
|
||||||
|
#include <ngx_stream.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ngx_addr_t *addr;
|
||||||
|
ngx_stream_complex_value_t *addr_value;
|
||||||
|
} ngx_stream_pass_srv_conf_t;
|
||||||
|
|
||||||
|
|
||||||
|
static void ngx_stream_pass_handler(ngx_stream_session_t *s);
|
||||||
|
static ngx_int_t ngx_stream_pass_match(ngx_listening_t *ls, ngx_addr_t *addr);
|
||||||
|
static void *ngx_stream_pass_create_srv_conf(ngx_conf_t *cf);
|
||||||
|
static char *ngx_stream_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_command_t ngx_stream_pass_commands[] = {
|
||||||
|
|
||||||
|
{ ngx_string("pass"),
|
||||||
|
NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||||
|
ngx_stream_pass,
|
||||||
|
NGX_STREAM_SRV_CONF_OFFSET,
|
||||||
|
0,
|
||||||
|
NULL },
|
||||||
|
|
||||||
|
ngx_null_command
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_stream_module_t ngx_stream_pass_module_ctx = {
|
||||||
|
NULL, /* preconfiguration */
|
||||||
|
NULL, /* postconfiguration */
|
||||||
|
|
||||||
|
NULL, /* create main configuration */
|
||||||
|
NULL, /* init main configuration */
|
||||||
|
|
||||||
|
ngx_stream_pass_create_srv_conf, /* create server configuration */
|
||||||
|
NULL /* merge server configuration */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ngx_module_t ngx_stream_pass_module = {
|
||||||
|
NGX_MODULE_V1,
|
||||||
|
&ngx_stream_pass_module_ctx, /* module context */
|
||||||
|
ngx_stream_pass_commands, /* module directives */
|
||||||
|
NGX_STREAM_MODULE, /* module type */
|
||||||
|
NULL, /* init master */
|
||||||
|
NULL, /* init module */
|
||||||
|
NULL, /* init process */
|
||||||
|
NULL, /* init thread */
|
||||||
|
NULL, /* exit thread */
|
||||||
|
NULL, /* exit process */
|
||||||
|
NULL, /* exit master */
|
||||||
|
NGX_MODULE_V1_PADDING
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ngx_stream_pass_handler(ngx_stream_session_t *s)
|
||||||
|
{
|
||||||
|
ngx_url_t u;
|
||||||
|
ngx_str_t url;
|
||||||
|
ngx_addr_t *addr;
|
||||||
|
ngx_uint_t i;
|
||||||
|
ngx_listening_t *ls;
|
||||||
|
ngx_connection_t *c;
|
||||||
|
ngx_stream_pass_srv_conf_t *pscf;
|
||||||
|
|
||||||
|
c = s->connection;
|
||||||
|
|
||||||
|
c->log->action = "passing connection to port";
|
||||||
|
|
||||||
|
if (c->buffer && c->buffer->pos != c->buffer->last) {
|
||||||
|
ngx_log_error(NGX_LOG_ERR, c->log, 0,
|
||||||
|
"cannot pass connection with preread data");
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_pass_module);
|
||||||
|
|
||||||
|
addr = pscf->addr;
|
||||||
|
|
||||||
|
if (addr == NULL) {
|
||||||
|
if (ngx_stream_complex_value(s, pscf->addr_value, &url) != NGX_OK) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_memzero(&u, sizeof(ngx_url_t));
|
||||||
|
|
||||||
|
u.url = url;
|
||||||
|
u.no_resolve = 1;
|
||||||
|
|
||||||
|
if (ngx_parse_url(c->pool, &u) != NGX_OK) {
|
||||||
|
if (u.err) {
|
||||||
|
ngx_log_error(NGX_LOG_ERR, c->log, 0,
|
||||||
|
"%s in pass \"%V\"", u.err, &u.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (u.naddrs == 0) {
|
||||||
|
ngx_log_error(NGX_LOG_ERR, c->log, 0,
|
||||||
|
"no addresses in pass \"%V\"", &u.url);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (u.no_port) {
|
||||||
|
ngx_log_error(NGX_LOG_ERR, c->log, 0,
|
||||||
|
"no port in pass \"%V\"", &u.url);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = &u.addrs[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
|
||||||
|
"stream pass addr: \"%V\"", &addr->name);
|
||||||
|
|
||||||
|
ls = ngx_cycle->listening.elts;
|
||||||
|
|
||||||
|
for (i = 0; i < ngx_cycle->listening.nelts; i++) {
|
||||||
|
|
||||||
|
if (ngx_stream_pass_match(&ls[i], addr) != NGX_OK) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->listening = &ls[i];
|
||||||
|
|
||||||
|
c->data = NULL;
|
||||||
|
c->buffer = NULL;
|
||||||
|
|
||||||
|
*c->log = c->listening->log;
|
||||||
|
c->log->handler = NULL;
|
||||||
|
c->log->data = NULL;
|
||||||
|
|
||||||
|
c->local_sockaddr = addr->sockaddr;
|
||||||
|
c->local_socklen = addr->socklen;
|
||||||
|
|
||||||
|
c->listening->handler(c);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_log_error(NGX_LOG_ERR, c->log, 0,
|
||||||
|
"port not found for \"%V\"", &addr->name);
|
||||||
|
|
||||||
|
ngx_stream_finalize_session(s, NGX_STREAM_OK);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
failed:
|
||||||
|
|
||||||
|
ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_int_t
|
||||||
|
ngx_stream_pass_match(ngx_listening_t *ls, ngx_addr_t *addr)
|
||||||
|
{
|
||||||
|
if (!ls->wildcard) {
|
||||||
|
return ngx_cmp_sockaddr(ls->sockaddr, ls->socklen,
|
||||||
|
addr->sockaddr, addr->socklen, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ls->sockaddr->sa_family == addr->sockaddr->sa_family
|
||||||
|
&& ngx_inet_get_port(ls->sockaddr) == ngx_inet_get_port(addr->sockaddr))
|
||||||
|
{
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NGX_DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void *
|
||||||
|
ngx_stream_pass_create_srv_conf(ngx_conf_t *cf)
|
||||||
|
{
|
||||||
|
ngx_stream_pass_srv_conf_t *conf;
|
||||||
|
|
||||||
|
conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_pass_srv_conf_t));
|
||||||
|
if (conf == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set by ngx_pcalloc():
|
||||||
|
*
|
||||||
|
* conf->addr = NULL;
|
||||||
|
* conf->addr_value = NULL;
|
||||||
|
*/
|
||||||
|
|
||||||
|
return conf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *
|
||||||
|
ngx_stream_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||||
|
{
|
||||||
|
ngx_stream_pass_srv_conf_t *pscf = conf;
|
||||||
|
|
||||||
|
ngx_url_t u;
|
||||||
|
ngx_str_t *value, *url;
|
||||||
|
ngx_stream_complex_value_t cv;
|
||||||
|
ngx_stream_core_srv_conf_t *cscf;
|
||||||
|
ngx_stream_compile_complex_value_t ccv;
|
||||||
|
|
||||||
|
if (pscf->addr || pscf->addr_value) {
|
||||||
|
return "is duplicate";
|
||||||
|
}
|
||||||
|
|
||||||
|
cscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_core_module);
|
||||||
|
|
||||||
|
cscf->handler = ngx_stream_pass_handler;
|
||||||
|
|
||||||
|
value = cf->args->elts;
|
||||||
|
|
||||||
|
url = &value[1];
|
||||||
|
|
||||||
|
ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
|
||||||
|
|
||||||
|
ccv.cf = cf;
|
||||||
|
ccv.value = url;
|
||||||
|
ccv.complex_value = &cv;
|
||||||
|
|
||||||
|
if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
|
||||||
|
return NGX_CONF_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cv.lengths) {
|
||||||
|
pscf->addr_value = ngx_palloc(cf->pool,
|
||||||
|
sizeof(ngx_stream_complex_value_t));
|
||||||
|
if (pscf->addr_value == NULL) {
|
||||||
|
return NGX_CONF_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pscf->addr_value = cv;
|
||||||
|
|
||||||
|
return NGX_CONF_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_memzero(&u, sizeof(ngx_url_t));
|
||||||
|
|
||||||
|
u.url = *url;
|
||||||
|
u.no_resolve = 1;
|
||||||
|
|
||||||
|
if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
|
||||||
|
if (u.err) {
|
||||||
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||||
|
"%s in \"%V\" of the \"pass\" directive",
|
||||||
|
u.err, &u.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NGX_CONF_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (u.naddrs == 0) {
|
||||||
|
return "has no addresses";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (u.no_port) {
|
||||||
|
return "has no port";
|
||||||
|
}
|
||||||
|
|
||||||
|
pscf->addr = &u.addrs[0];
|
||||||
|
|
||||||
|
return NGX_CONF_OK;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user