changeset 6643:9757cffc1e2f

Stream: variables in proxy_pass and proxy_ssl_name.
author Vladimir Homutov <vl@nginx.com>
date Tue, 14 Jun 2016 18:29:46 +0300
parents 72282dd5884e
children af642539cd53
files src/stream/ngx_stream_proxy_module.c src/stream/ngx_stream_upstream.h src/stream/ngx_stream_upstream_round_robin.c src/stream/ngx_stream_upstream_round_robin.h
diffstat 4 files changed, 483 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/src/stream/ngx_stream_proxy_module.c
+++ b/src/stream/ngx_stream_proxy_module.c
@@ -37,7 +37,7 @@ typedef struct {
     ngx_flag_t                       ssl_session_reuse;
     ngx_uint_t                       ssl_protocols;
     ngx_str_t                        ssl_ciphers;
-    ngx_str_t                        ssl_name;
+    ngx_stream_complex_value_t      *ssl_name;
     ngx_flag_t                       ssl_server_name;
 
     ngx_flag_t                       ssl_verify;
@@ -52,14 +52,18 @@ typedef struct {
 #endif
 
     ngx_stream_upstream_srv_conf_t  *upstream;
+    ngx_stream_complex_value_t      *upstream_value;
 } ngx_stream_proxy_srv_conf_t;
 
 
 static void ngx_stream_proxy_handler(ngx_stream_session_t *s);
+static ngx_int_t ngx_stream_proxy_eval(ngx_stream_session_t *s,
+    ngx_stream_proxy_srv_conf_t *pscf);
 static ngx_int_t ngx_stream_proxy_set_local(ngx_stream_session_t *s,
     ngx_stream_upstream_t *u, ngx_stream_upstream_local_t *local);
 static void ngx_stream_proxy_connect(ngx_stream_session_t *s);
 static void ngx_stream_proxy_init_upstream(ngx_stream_session_t *s);
+static void ngx_stream_proxy_resolve_handler(ngx_resolver_ctx_t *ctx);
 static void ngx_stream_proxy_upstream_handler(ngx_event_t *ev);
 static void ngx_stream_proxy_downstream_handler(ngx_event_t *ev);
 static void ngx_stream_proxy_process_connection(ngx_event_t *ev,
@@ -246,7 +250,7 @@ static ngx_command_t  ngx_stream_proxy_c
 
     { ngx_string("proxy_ssl_name"),
       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_str_slot,
+      ngx_stream_set_complex_value_slot,
       NGX_STREAM_SRV_CONF_OFFSET,
       offsetof(ngx_stream_proxy_srv_conf_t, ssl_name),
       NULL },
@@ -344,11 +348,16 @@ ngx_module_t  ngx_stream_proxy_module = 
 static void
 ngx_stream_proxy_handler(ngx_stream_session_t *s)
 {
-    u_char                          *p;
-    ngx_connection_t                *c;
-    ngx_stream_upstream_t           *u;
-    ngx_stream_proxy_srv_conf_t     *pscf;
-    ngx_stream_upstream_srv_conf_t  *uscf;
+    u_char                           *p;
+    ngx_str_t                        *host;
+    ngx_uint_t                        i;
+    ngx_connection_t                 *c;
+    ngx_resolver_ctx_t               *ctx, temp;
+    ngx_stream_upstream_t            *u;
+    ngx_stream_core_srv_conf_t       *cscf;
+    ngx_stream_proxy_srv_conf_t      *pscf;
+    ngx_stream_upstream_srv_conf_t   *uscf, **uscfp;
+    ngx_stream_upstream_main_conf_t  *umcf;
 
     c = s->connection;
 
@@ -377,7 +386,161 @@ ngx_stream_proxy_handler(ngx_stream_sess
 
     u->peer.type = c->type;
 
-    uscf = pscf->upstream;
+    u->proxy_protocol = pscf->proxy_protocol;
+    u->start_sec = ngx_time();
+
+    c->write->handler = ngx_stream_proxy_downstream_handler;
+    c->read->handler = ngx_stream_proxy_downstream_handler;
+
+    if (c->type == SOCK_STREAM) {
+        p = ngx_pnalloc(c->pool, pscf->buffer_size);
+        if (p == NULL) {
+            ngx_stream_proxy_finalize(s, NGX_ERROR);
+            return;
+        }
+
+        u->downstream_buf.start = p;
+        u->downstream_buf.end = p + pscf->buffer_size;
+        u->downstream_buf.pos = p;
+        u->downstream_buf.last = p;
+
+        if (u->proxy_protocol
+#if (NGX_STREAM_SSL)
+            && pscf->ssl == NULL
+#endif
+            && pscf->buffer_size >= NGX_PROXY_PROTOCOL_MAX_HEADER)
+        {
+            /* optimization for a typical case */
+
+            ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
+                           "stream proxy send PROXY protocol header");
+
+            p = ngx_proxy_protocol_write(c, u->downstream_buf.last,
+                                         u->downstream_buf.end);
+            if (p == NULL) {
+                ngx_stream_proxy_finalize(s, NGX_ERROR);
+                return;
+            }
+
+            u->downstream_buf.last = p;
+            u->proxy_protocol = 0;
+        }
+
+        if (c->read->ready) {
+            ngx_post_event(c->read, &ngx_posted_events);
+        }
+    }
+
+    if (pscf->upstream_value) {
+        if (ngx_stream_proxy_eval(s, pscf) != NGX_OK) {
+            ngx_stream_proxy_finalize(s, NGX_ERROR);
+            return;
+        }
+    }
+
+    if (u->resolved == NULL) {
+
+        uscf = pscf->upstream;
+
+    } else {
+
+#if (NGX_STREAM_SSL)
+        u->ssl_name = u->resolved->host;
+#endif
+
+        host = &u->resolved->host;
+
+        if (u->resolved->sockaddr) {
+
+            if (u->resolved->port == 0
+                && u->resolved->sockaddr->sa_family != AF_UNIX)
+            {
+                ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                              "no port in upstream \"%V\"", host);
+                ngx_stream_proxy_finalize(s, NGX_ERROR);
+                return;
+            }
+
+            if (ngx_stream_upstream_create_round_robin_peer(s, u->resolved)
+                != NGX_OK)
+            {
+                ngx_stream_proxy_finalize(s, NGX_ERROR);
+                return;
+            }
+
+            ngx_stream_proxy_connect(s);
+
+            return;
+        }
+
+        umcf = ngx_stream_get_module_main_conf(s, ngx_stream_upstream_module);
+
+        uscfp = umcf->upstreams.elts;
+
+        for (i = 0; i < umcf->upstreams.nelts; i++) {
+
+            uscf = uscfp[i];
+
+            if (uscf->host.len == host->len
+                && ((uscf->port == 0 && u->resolved->no_port)
+                     || uscf->port == u->resolved->port)
+                && ngx_strncasecmp(uscf->host.data, host->data, host->len) == 0)
+            {
+                goto found;
+            }
+        }
+
+        if (u->resolved->port == 0) {
+            ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                          "no port in upstream \"%V\"", host);
+            ngx_stream_proxy_finalize(s, NGX_ERROR);
+            return;
+        }
+
+        temp.name = *host;
+
+        cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);
+
+        ctx = ngx_resolve_start(cscf->resolver, &temp);
+        if (ctx == NULL) {
+            ngx_stream_proxy_finalize(s, NGX_ERROR);
+            return;
+        }
+
+        if (ctx == NGX_NO_RESOLVER) {
+            ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                          "no resolver defined to resolve %V", host);
+            ngx_stream_proxy_finalize(s, NGX_ERROR);
+            return;
+        }
+
+        ctx->name = *host;
+        ctx->handler = ngx_stream_proxy_resolve_handler;
+        ctx->data = s;
+        ctx->timeout = cscf->resolver_timeout;
+
+        u->resolved->ctx = ctx;
+
+        if (ngx_resolve_name(ctx) != NGX_OK) {
+            u->resolved->ctx = NULL;
+            ngx_stream_proxy_finalize(s, NGX_ERROR);
+            return;
+        }
+
+        return;
+    }
+
+found:
+
+    if (uscf == NULL) {
+        ngx_log_error(NGX_LOG_ALERT, c->log, 0, "no upstream configuration");
+        ngx_stream_proxy_finalize(s, NGX_ERROR);
+        return;
+    }
+
+#if (NGX_HTTP_SSL)
+    u->ssl_name = uscf->host;
+#endif
 
     if (uscf->peer.init(s, uscf) != NGX_OK) {
         ngx_stream_proxy_finalize(s, NGX_ERROR);
@@ -392,55 +555,58 @@ ngx_stream_proxy_handler(ngx_stream_sess
         u->peer.tries = pscf->next_upstream_tries;
     }
 
-    u->proxy_protocol = pscf->proxy_protocol;
-    u->start_sec = ngx_time();
-
-    c->write->handler = ngx_stream_proxy_downstream_handler;
-    c->read->handler = ngx_stream_proxy_downstream_handler;
-
-    if (c->type == SOCK_DGRAM) {
-        ngx_stream_proxy_connect(s);
-        return;
-    }
-
-    p = ngx_pnalloc(c->pool, pscf->buffer_size);
-    if (p == NULL) {
-        ngx_stream_proxy_finalize(s, NGX_ERROR);
-        return;
+    ngx_stream_proxy_connect(s);
+}
+
+
+static ngx_int_t
+ngx_stream_proxy_eval(ngx_stream_session_t *s,
+    ngx_stream_proxy_srv_conf_t *pscf)
+{
+    ngx_str_t               host;
+    ngx_url_t               url;
+    ngx_stream_upstream_t  *u;
+
+    if (ngx_stream_complex_value(s, pscf->upstream_value, &host) != NGX_OK) {
+        return NGX_ERROR;
     }
 
-    u->downstream_buf.start = p;
-    u->downstream_buf.end = p + pscf->buffer_size;
-    u->downstream_buf.pos = p;
-    u->downstream_buf.last = p;
-
-    if (u->proxy_protocol
-#if (NGX_STREAM_SSL)
-        && pscf->ssl == NULL
-#endif
-        && pscf->buffer_size >= NGX_PROXY_PROTOCOL_MAX_HEADER)
-    {
-        /* optimization for a typical case */
-
-        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
-                       "stream proxy send PROXY protocol header");
-
-        p = ngx_proxy_protocol_write(c, u->downstream_buf.last,
-                                     u->downstream_buf.end);
-        if (p == NULL) {
-            ngx_stream_proxy_finalize(s, NGX_ERROR);
-            return;
+    ngx_memzero(&url, sizeof(ngx_url_t));
+
+    url.url = host;
+    url.no_resolve = 1;
+
+    if (ngx_parse_url(s->connection->pool, &url) != NGX_OK) {
+        if (url.err) {
+            ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+                          "%s in upstream \"%V\"", url.err, &url.url);
         }
 
-        u->downstream_buf.last = p;
-        u->proxy_protocol = 0;
+        return NGX_ERROR;
+    }
+
+    u = s->upstream;
+
+    u->resolved = ngx_pcalloc(s->connection->pool,
+                              sizeof(ngx_stream_upstream_resolved_t));
+    if (u->resolved == NULL) {
+        return NGX_ERROR;
     }
 
-    if (c->read->ready) {
-        ngx_post_event(c->read, &ngx_posted_events);
+    if (url.addrs && url.addrs[0].sockaddr) {
+        u->resolved->sockaddr = url.addrs[0].sockaddr;
+        u->resolved->socklen = url.addrs[0].socklen;
+        u->resolved->naddrs = 1;
+        u->resolved->host = url.addrs[0].name;
+
+    } else {
+        u->resolved->host = url.host;
     }
 
-    ngx_stream_proxy_connect(s);
+    u->resolved->port = url.port;
+    u->resolved->no_port = url.no_port;
+
+    return NGX_OK;
 }
 
 
@@ -883,10 +1049,13 @@ ngx_stream_proxy_ssl_name(ngx_stream_ses
 
     u = s->upstream;
 
-    name = pscf->ssl_name;
-
-    if (name.len == 0) {
-        name = pscf->upstream->host;
+    if (pscf->ssl_name) {
+        if (ngx_stream_complex_value(s, pscf->ssl_name, &name) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+    } else {
+        name = u->ssl_name;
     }
 
     if (name.len == 0) {
@@ -976,6 +1145,75 @@ ngx_stream_proxy_downstream_handler(ngx_
 
 
 static void
+ngx_stream_proxy_resolve_handler(ngx_resolver_ctx_t *ctx)
+{
+    ngx_stream_session_t            *s;
+    ngx_stream_upstream_t           *u;
+    ngx_stream_proxy_srv_conf_t     *pscf;
+    ngx_stream_upstream_resolved_t  *ur;
+
+    s = ctx->data;
+
+    u = s->upstream;
+    ur = u->resolved;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0,
+                   "stream upstream resolve");
+
+    if (ctx->state) {
+        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+                      "%V could not be resolved (%i: %s)",
+                      &ctx->name, ctx->state,
+                      ngx_resolver_strerror(ctx->state));
+
+        ngx_stream_proxy_finalize(s, NGX_ERROR);
+        return;
+    }
+
+    ur->naddrs = ctx->naddrs;
+    ur->addrs = ctx->addrs;
+
+#if (NGX_DEBUG)
+    {
+    u_char      text[NGX_SOCKADDR_STRLEN];
+    ngx_str_t   addr;
+    ngx_uint_t  i;
+
+    addr.data = text;
+
+    for (i = 0; i < ctx->naddrs; i++) {
+        addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen,
+                                 text, NGX_SOCKADDR_STRLEN, 0);
+
+        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
+                       "name was resolved to %V", &addr);
+    }
+    }
+#endif
+
+    if (ngx_stream_upstream_create_round_robin_peer(s, ur) != NGX_OK) {
+        ngx_stream_proxy_finalize(s, NGX_ERROR);
+        return;
+    }
+
+    ngx_resolve_name_done(ctx);
+    ur->ctx = NULL;
+
+    u->peer.start_time = ngx_current_msec;
+
+    pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);
+
+    if (pscf->next_upstream_tries
+        && u->peer.tries > pscf->next_upstream_tries)
+    {
+        u->peer.tries = pscf->next_upstream_tries;
+    }
+
+    ngx_stream_proxy_connect(s);
+}
+
+
+static void
 ngx_stream_proxy_upstream_handler(ngx_event_t *ev)
 {
     ngx_stream_proxy_process_connection(ev, !ev->write);
@@ -1397,6 +1635,11 @@ ngx_stream_proxy_finalize(ngx_stream_ses
         goto noupstream;
     }
 
+    if (u->resolved && u->resolved->ctx) {
+        ngx_resolve_name_done(u->resolved->ctx);
+        u->resolved->ctx = NULL;
+    }
+
     if (u->peer.free && u->peer.sockaddr) {
         u->peer.free(&u->peer, u->peer.data, 0);
         u->peer.sockaddr = NULL;
@@ -1471,7 +1714,7 @@ ngx_stream_proxy_create_srv_conf(ngx_con
      *
      *     conf->ssl_protocols = 0;
      *     conf->ssl_ciphers = { 0, NULL };
-     *     conf->ssl_name = { 0, NULL };
+     *     conf->ssl_name = NULL;
      *     conf->ssl_trusted_certificate = { 0, NULL };
      *     conf->ssl_crl = { 0, NULL };
      *     conf->ssl_certificate = { 0, NULL };
@@ -1479,6 +1722,7 @@ ngx_stream_proxy_create_srv_conf(ngx_con
      *
      *     conf->ssl = NULL;
      *     conf->upstream = NULL;
+     *     conf->upstream_value = NULL;
      */
 
     conf->connect_timeout = NGX_CONF_UNSET_MSEC;
@@ -1555,7 +1799,9 @@ ngx_stream_proxy_merge_srv_conf(ngx_conf
 
     ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT");
 
-    ngx_conf_merge_str_value(conf->ssl_name, prev->ssl_name, "");
+    if (conf->ssl_name == NULL) {
+        conf->ssl_name = prev->ssl_name;
+    }
 
     ngx_conf_merge_value(conf->ssl_server_name, prev->ssl_server_name, 0);
 
@@ -1665,11 +1911,13 @@ ngx_stream_proxy_pass(ngx_conf_t *cf, ng
 {
     ngx_stream_proxy_srv_conf_t *pscf = conf;
 
-    ngx_url_t                    u;
-    ngx_str_t                   *value, *url;
-    ngx_stream_core_srv_conf_t  *cscf;
-
-    if (pscf->upstream) {
+    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->upstream || pscf->upstream_value) {
         return "is duplicate";
     }
 
@@ -1681,6 +1929,28 @@ ngx_stream_proxy_pass(ngx_conf_t *cf, ng
 
     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->upstream_value = ngx_palloc(cf->pool,
+                                          sizeof(ngx_stream_complex_value_t));
+        if (pscf->upstream_value == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        *pscf->upstream_value = cv;
+
+        return NGX_CONF_OK;
+    }
+
     ngx_memzero(&u, sizeof(ngx_url_t));
 
     u.url = *url;
--- a/src/stream/ngx_stream_upstream.h
+++ b/src/stream/ngx_stream_upstream.h
@@ -79,6 +79,21 @@ struct ngx_stream_upstream_srv_conf_s {
 
 
 typedef struct {
+    ngx_str_t                          host;
+    in_port_t                          port;
+    ngx_uint_t                         no_port; /* unsigned no_port:1 */
+
+    ngx_uint_t                         naddrs;
+    ngx_resolver_addr_t               *addrs;
+
+    struct sockaddr                   *sockaddr;
+    socklen_t                          socklen;
+
+    ngx_resolver_ctx_t                *ctx;
+} ngx_stream_upstream_resolved_t;
+
+
+typedef struct {
     ngx_peer_connection_t              peer;
     ngx_buf_t                          downstream_buf;
     ngx_buf_t                          upstream_buf;
@@ -88,6 +103,7 @@ typedef struct {
 #if (NGX_STREAM_SSL)
     ngx_str_t                          ssl_name;
 #endif
+    ngx_stream_upstream_resolved_t    *resolved;
     unsigned                           connected:1;
     unsigned                           proxy_protocol:1;
 } ngx_stream_upstream_t;
--- a/src/stream/ngx_stream_upstream_round_robin.c
+++ b/src/stream/ngx_stream_upstream_round_robin.c
@@ -23,6 +23,10 @@ static ngx_int_t ngx_stream_upstream_set
     ngx_peer_connection_t *pc, void *data);
 static void ngx_stream_upstream_save_round_robin_peer_session(
     ngx_peer_connection_t *pc, void *data);
+static ngx_int_t ngx_stream_upstream_empty_set_session(
+    ngx_peer_connection_t *pc, void *data);
+static void ngx_stream_upstream_empty_save_session(ngx_peer_connection_t *pc,
+    void *data);
 
 #endif
 
@@ -293,6 +297,123 @@ ngx_stream_upstream_init_round_robin_pee
 
 
 ngx_int_t
+ngx_stream_upstream_create_round_robin_peer(ngx_stream_session_t *s,
+    ngx_stream_upstream_resolved_t *ur)
+{
+    u_char                              *p;
+    size_t                               len;
+    socklen_t                            socklen;
+    ngx_uint_t                           i, n;
+    struct sockaddr                     *sockaddr;
+    ngx_stream_upstream_rr_peer_t       *peer, **peerp;
+    ngx_stream_upstream_rr_peers_t      *peers;
+    ngx_stream_upstream_rr_peer_data_t  *rrp;
+
+    rrp = s->upstream->peer.data;
+
+    if (rrp == NULL) {
+        rrp = ngx_palloc(s->connection->pool,
+                         sizeof(ngx_stream_upstream_rr_peer_data_t));
+        if (rrp == NULL) {
+            return NGX_ERROR;
+        }
+
+        s->upstream->peer.data = rrp;
+    }
+
+    peers = ngx_pcalloc(s->connection->pool,
+                        sizeof(ngx_stream_upstream_rr_peers_t));
+    if (peers == NULL) {
+        return NGX_ERROR;
+    }
+
+    peer = ngx_pcalloc(s->connection->pool,
+                       sizeof(ngx_stream_upstream_rr_peer_t) * ur->naddrs);
+    if (peer == NULL) {
+        return NGX_ERROR;
+    }
+
+    peers->single = (ur->naddrs == 1);
+    peers->number = ur->naddrs;
+    peers->name = &ur->host;
+
+    if (ur->sockaddr) {
+        peer[0].sockaddr = ur->sockaddr;
+        peer[0].socklen = ur->socklen;
+        peer[0].name = ur->host;
+        peer[0].weight = 1;
+        peer[0].effective_weight = 1;
+        peer[0].current_weight = 0;
+        peer[0].max_fails = 1;
+        peer[0].fail_timeout = 10;
+        peers->peer = peer;
+
+    } else {
+        peerp = &peers->peer;
+
+        for (i = 0; i < ur->naddrs; i++) {
+
+            socklen = ur->addrs[i].socklen;
+
+            sockaddr = ngx_palloc(s->connection->pool, socklen);
+            if (sockaddr == NULL) {
+                return NGX_ERROR;
+            }
+
+            ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen);
+            ngx_inet_set_port(sockaddr, ur->port);
+
+            p = ngx_pnalloc(s->connection->pool, NGX_SOCKADDR_STRLEN);
+            if (p == NULL) {
+                return NGX_ERROR;
+            }
+
+            len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);
+
+            peer[i].sockaddr = sockaddr;
+            peer[i].socklen = socklen;
+            peer[i].name.len = len;
+            peer[i].name.data = p;
+            peer[i].weight = 1;
+            peer[i].effective_weight = 1;
+            peer[i].current_weight = 0;
+            peer[i].max_fails = 1;
+            peer[i].fail_timeout = 10;
+            *peerp = &peer[i];
+            peerp = &peer[i].next;
+        }
+    }
+
+    rrp->peers = peers;
+    rrp->current = NULL;
+
+    if (rrp->peers->number <= 8 * sizeof(uintptr_t)) {
+        rrp->tried = &rrp->data;
+        rrp->data = 0;
+
+    } else {
+        n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
+                / (8 * sizeof(uintptr_t));
+
+        rrp->tried = ngx_pcalloc(s->connection->pool, n * sizeof(uintptr_t));
+        if (rrp->tried == NULL) {
+            return NGX_ERROR;
+        }
+    }
+
+    s->upstream->peer.get = ngx_stream_upstream_get_round_robin_peer;
+    s->upstream->peer.free = ngx_stream_upstream_free_round_robin_peer;
+    s->upstream->peer.tries = ngx_stream_upstream_tries(rrp->peers);
+#if (NGX_STREAM_SSL)
+    s->upstream->peer.set_session = ngx_stream_upstream_empty_set_session;
+    s->upstream->peer.save_session = ngx_stream_upstream_empty_save_session;
+#endif
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
 ngx_stream_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
 {
     ngx_stream_upstream_rr_peer_data_t *rrp = data;
@@ -699,4 +820,18 @@ ngx_stream_upstream_save_round_robin_pee
     }
 }
 
+
+static ngx_int_t
+ngx_stream_upstream_empty_set_session(ngx_peer_connection_t *pc, void *data)
+{
+    return NGX_OK;
+}
+
+
+static void
+ngx_stream_upstream_empty_save_session(ngx_peer_connection_t *pc, void *data)
+{
+    return;
+}
+
 #endif
--- a/src/stream/ngx_stream_upstream_round_robin.h
+++ b/src/stream/ngx_stream_upstream_round_robin.h
@@ -130,6 +130,8 @@ ngx_int_t ngx_stream_upstream_init_round
     ngx_stream_upstream_srv_conf_t *us);
 ngx_int_t ngx_stream_upstream_init_round_robin_peer(ngx_stream_session_t *s,
     ngx_stream_upstream_srv_conf_t *us);
+ngx_int_t ngx_stream_upstream_create_round_robin_peer(ngx_stream_session_t *s,
+    ngx_stream_upstream_resolved_t *ur);
 ngx_int_t ngx_stream_upstream_get_round_robin_peer(ngx_peer_connection_t *pc,
     void *data);
 void ngx_stream_upstream_free_round_robin_peer(ngx_peer_connection_t *pc,