1 /*
2  * Copyright (c) 2015 Tatsuhiko Kubo (cubicdaiya@gmail.com>)
3  * Copyright (C) 2018 Aleksei Konovkin (alkon2000@mail.ru)
4  */
5 
6 extern "C" {
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_http.h>
11 #include <ngx_inet.h>
12 
13 
14 #include <assert.h>
15 
16 }
17 
18 #include "ngx_dynamic_upstream_module.h"
19 #include "ngx_dynamic_upstream_op.h"
20 
21 
22 template <class S> static ngx_int_t
23 ngx_dynamic_upstream_op_add(typename TypeSelect<S>::peers_type *primary,
24     ngx_dynamic_upstream_op_t *op, ngx_slab_pool_t *shpool,
25     ngx_pool_t *temp_pool, ngx_log_t *log);
26 
27 
28 template <class S> static ngx_int_t
29 ngx_dynamic_upstream_op_sync(typename TypeSelect<S>::peers_type *primary,
30     ngx_dynamic_upstream_op_t *op, ngx_slab_pool_t *shpool,
31     ngx_pool_t *temp_pool, ngx_log_t *log);
32 
33 
34 template <class S> static ngx_int_t
35 ngx_dynamic_upstream_op_del(typename TypeSelect<S>::peers_type *primary,
36     ngx_dynamic_upstream_op_t *op, ngx_slab_pool_t *shpool,
37     ngx_pool_t *temp_pool, ngx_log_t *log);
38 
39 
40 template <class S> static ngx_int_t
41 ngx_dynamic_upstream_op_update(typename TypeSelect<S>::peers_type *primary,
42     ngx_dynamic_upstream_op_t *op, ngx_log_t *log);
43 
44 
45 template <class S> static ngx_int_t
46 ngx_dynamic_upstream_op_hash(typename TypeSelect<S>::peers_type *primary,
47     ngx_dynamic_upstream_op_t *op);
48 
49 
50 template <class T> T*
ngx_shm_calloc(ngx_slab_pool_t * shpool,size_t size=0)51 ngx_shm_calloc(ngx_slab_pool_t *shpool, size_t size = 0)
52 {
53     return (T*) ngx_slab_calloc(shpool, size == 0 ? sizeof(T) : size);
54 }
55 
56 
57 #define CALL(fun, peers, ...)                                          \
58    (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_STREAM                 \
59         ? fun<ngx_stream_upstream_srv_conf_t>(                         \
60             (ngx_stream_upstream_rr_peers_t *) (peers), __VA_ARGS__)   \
61         : fun<ngx_http_upstream_srv_conf_t>(                           \
62             (ngx_http_upstream_rr_peers_t *) (peers), __VA_ARGS__))
63 
64 
65 ngx_int_t
ngx_dynamic_upstream_op_impl(ngx_log_t * log,ngx_dynamic_upstream_op_t * op,ngx_slab_pool_t * shpool,ngx_pool_t * temp_pool,void * peers)66 ngx_dynamic_upstream_op_impl(ngx_log_t *log, ngx_dynamic_upstream_op_t *op,
67     ngx_slab_pool_t *shpool, ngx_pool_t *temp_pool, void *peers)
68 {
69     ngx_int_t rc = NGX_OK;
70 
71     op->status = NGX_HTTP_OK;
72     op->err = "unexpected";
73 
74     switch (op->op) {
75 
76         case NGX_DYNAMIC_UPSTEAM_OP_ADD:
77             rc = CALL(ngx_dynamic_upstream_op_add, peers, op, shpool,
78                       temp_pool, log);
79             break;
80 
81         case NGX_DYNAMIC_UPSTEAM_OP_REMOVE:
82             rc = CALL(ngx_dynamic_upstream_op_del, peers, op, shpool,
83                       temp_pool, log);
84             break;
85 
86         case NGX_DYNAMIC_UPSTEAM_OP_SYNC:
87             rc = CALL(ngx_dynamic_upstream_op_sync, peers, op, shpool,
88                       temp_pool, log);
89             break;
90 
91         case NGX_DYNAMIC_UPSTEAM_OP_PARAM:
92             rc = CALL(ngx_dynamic_upstream_op_update, peers, op, log);
93             break;
94 
95         case NGX_DYNAMIC_UPSTEAM_OP_HASH:
96             rc = CALL(ngx_dynamic_upstream_op_hash, peers, op);
97             break;
98 
99         case NGX_DYNAMIC_UPSTEAM_OP_LIST:
100         default:
101             rc = NGX_OK;
102             break;
103 
104     }
105 
106     return rc;
107 }
108 
109 
110 static const ngx_str_t
111 noaddr = ngx_string("0.0.0.0:1");
112 
113 
114 ngx_int_t
is_reserved_addr(ngx_str_t * addr)115 is_reserved_addr(ngx_str_t *addr)
116 {
117     return addr->len >= noaddr.len
118            && ngx_memcmp(addr->data, noaddr.data, noaddr.len - 2) == 0;
119 }
120 
121 static ngx_int_t
ngx_dynamic_upstream_parse_url(ngx_url_t * u,ngx_pool_t * pool,ngx_dynamic_upstream_op_t * op)122 ngx_dynamic_upstream_parse_url(ngx_url_t *u, ngx_pool_t *pool,
123     ngx_dynamic_upstream_op_t *op)
124 {
125     ngx_memzero(u, sizeof(ngx_url_t));
126 
127     u->url = op->server;
128     u->default_port = 80;
129     u->no_resolve = op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_RESOLVE_SYNC
130         ? 0 : 1;
131 
132     if (ngx_parse_url(pool, u) != NGX_OK) {
133 
134         op->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
135         if (u->err)
136             op->err = u->err;
137 
138         return NGX_ERROR;
139     }
140 
141     if (u->naddrs == 0) {
142 
143         if (u->no_resolve) {
144 
145             u->url = noaddr;
146 
147             if (ngx_parse_url(pool, u) != NGX_OK) {
148 
149                 op->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
150                 if (u->err)
151                     op->err = u->err;
152 
153                 return NGX_ERROR;
154             }
155 
156             u->url = op->server;
157             return NGX_AGAIN;
158 
159         } else {
160 
161             op->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
162             op->err = "failed to resolve";
163 
164             return NGX_ERROR;
165         }
166     }
167 
168     return NGX_OK;
169 }
170 
171 
172 template <class PeerT> static ngx_flag_t
equals(PeerT * peer,ngx_str_t server,ngx_str_t name)173 equals(PeerT *peer, ngx_str_t server, ngx_str_t name)
174 {
175     if (server.data && name.data)
176         return str_eq(server, peer->server) && str_eq(name, peer->name);
177 
178     assert(server.data != NULL);
179 
180     return str_eq(server, peer->server) || str_eq(server, peer->name);
181 }
182 
183 
184 static ngx_str_t
ngx_str_shm(ngx_slab_pool_t * shpool,ngx_str_t * s)185 ngx_str_shm(ngx_slab_pool_t *shpool, ngx_str_t *s)
186 {
187     ngx_str_t  sh = ngx_null_string;
188     sh.data = ngx_shm_calloc<u_char>(shpool, s->len);
189     if (sh.data != NULL) {
190         ngx_memcpy(sh.data, s->data, s->len);
191         sh.len = s->len;
192     }
193     return sh;
194 }
195 
196 
197 template <class S> struct SearchResult {
198     typename TypeSelect<S>::peers_type  *peers;
199     typename TypeSelect<S>::peer_type   *peer;
200     typename TypeSelect<S>::peer_type   *prev;
201 };
202 
203 
204 template <class S> static SearchResult<S>
search_peer(typename TypeSelect<S>::peers_type * primary,ngx_str_t server,ngx_str_t name,ngx_flag_t exact=0)205 search_peer(typename TypeSelect<S>::peers_type *primary,
206     ngx_str_t server, ngx_str_t name, ngx_flag_t exact = 0)
207 {
208     SearchResult<S>  rv;
209     ngx_uint_t       j;
210     ngx_flag_t       is_reserved = is_reserved_addr(&name);
211 
212     for (rv.peers = primary, j = 0, rv.prev = NULL;
213          rv.peers != NULL && j < 2;
214          rv.peers = rv.peers->next, rv.prev = NULL, j++) {
215 
216         for (rv.peer = rv.peers->peer;
217              rv.peer != NULL;
218              rv.prev = rv.peer, rv.peer = rv.peer->next) {
219 
220             if (equals<typename TypeSelect<S>::peer_type>(rv.peer, server, name)
221                 || (!exact && is_reserved && str_eq(rv.peer->server, server)))
222                 return rv;
223         }
224     }
225 
226     return rv;
227 }
228 
229 
230 template <class S> static ngx_int_t
ngx_dynamic_upstream_op_add_peer(ngx_log_t * log,ngx_dynamic_upstream_op_t * op,ngx_slab_pool_t * shpool,typename TypeSelect<S>::peers_type * primary,ngx_url_t * u,int i)231 ngx_dynamic_upstream_op_add_peer(ngx_log_t *log,
232     ngx_dynamic_upstream_op_t *op, ngx_slab_pool_t *shpool,
233     typename TypeSelect<S>::peers_type *primary, ngx_url_t *u, int i)
234 {
235     typename TypeSelect<S>::peers_type  *peers, *backup;
236     typename TypeSelect<S>::peer_type   *npeer;
237     SearchResult<S>                      found;
238 
239     peers = primary;
240     backup = primary->next;
241 
242     if (u->addrs[i].name.data[0] == '[' &&
243         !(op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_IPV6)) {
244 
245         op->status = NGX_HTTP_NOT_MODIFIED;
246         return NGX_OK;
247     }
248 
249     op->status = NGX_HTTP_OK;
250 
251     found = search_peer<S>(primary, op->server, u->addrs[i].name);
252     if (found.peer == NULL) {
253         if (op->backup)
254             goto backup;
255         goto add;
256     }
257 
258     if ((op->backup && found.peers == primary)
259         || (!op->backup && found.peers == backup)) {
260         op->status = NGX_HTTP_PRECONDITION_FAILED;
261         op->err = "can't change server type (primary<->backup)";
262         return NGX_ERROR;
263     }
264 
265     op->status = NGX_HTTP_NOT_MODIFIED;
266     op->err = "exists";
267 
268     return NGX_OK;
269 
270 backup:
271 
272     if (backup == NULL) {
273         backup = ngx_shm_calloc<typename TypeSelect<S>::peers_type>(shpool);
274         if (backup == NULL) {
275             op->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
276             op->err = "no shared memory";
277             return NGX_ERROR;
278         }
279         backup->shpool = primary->shpool;
280         backup->name = primary->name;
281     }
282 
283     peers = backup;
284 
285 add:
286 
287     npeer = ngx_shm_calloc<typename TypeSelect<S>::peer_type>(shpool);
288     if (npeer == NULL)
289         goto fail;
290 
291     npeer->server = ngx_str_shm(shpool, &u->url);
292     npeer->name = ngx_str_shm(shpool, &u->addrs[i].name);
293     npeer->sockaddr = ngx_shm_calloc<sockaddr>(shpool, u->addrs[i].socklen);
294 
295     if (npeer->server.data == NULL
296         || npeer->name.data == NULL
297         || npeer->sockaddr == NULL)
298         goto fail;
299 
300     npeer->socklen = u->addrs[i].socklen;
301     ngx_memcpy(npeer->sockaddr, u->addrs[i].sockaddr, npeer->socklen);
302 
303     if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_WEIGHT) {
304         npeer->weight = op->weight;
305         npeer->effective_weight = op->weight;
306     } else {
307         npeer->weight = 1;
308         npeer->effective_weight = 1;
309     }
310 
311     if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_MAX_FAILS)
312         npeer->max_fails = op->max_fails;
313     else
314         npeer->max_fails = primary->peer->max_fails;
315 
316     if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_FAIL_TIMEOUT)
317         npeer->fail_timeout = op->fail_timeout;
318     else
319         npeer->fail_timeout = primary->peer->fail_timeout;
320 
321     if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_MAX_CONNS)
322         npeer->max_conns = op->max_conns;
323     else
324         npeer->max_conns = primary->peer->max_conns;
325 
326     if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_DOWN)
327         npeer->down = op->down;
328 
329     npeer->next = peers->peer;
330     peers->peer = npeer;
331 
332     peers->total_weight += npeer->weight;
333     peers->single = (peers->number == 0);
334     peers->number++;
335     peers->weighted = (peers->total_weight != peers->number);
336 
337     primary->next = backup;
338 
339     if (!is_reserved_addr(&u->addrs[i].name)) {
340         ngx_log_error(NGX_LOG_NOTICE, log, 0, "%V: added server %V peer %V",
341                       &op->upstream, &u->url, &u->addrs[i].name);
342     } else {
343         ngx_log_error(NGX_LOG_NOTICE, log, 0,
344                       "%V: added server %V peer -.-.-.-",
345                       &op->upstream, &u->url);
346     }
347 
348     return NGX_OK;
349 
350 fail:
351 
352     if (npeer != NULL) {
353 
354         if (npeer->server.data != NULL)
355             ngx_slab_free(shpool, npeer->server.data);
356 
357         if (npeer->name.data != NULL)
358             ngx_slab_free(shpool, npeer->name.data);
359 
360         if (npeer->sockaddr != NULL)
361             ngx_slab_free(shpool, npeer->sockaddr);
362 
363         ngx_slab_free(shpool, npeer);
364     }
365 
366     if (backup != NULL && primary->next == NULL)
367         ngx_slab_free(shpool, backup);
368 
369     op->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
370     op->err = "no shared memory";
371 
372     return NGX_ERROR;
373 }
374 
375 
376 template <class S> static ngx_int_t
ngx_dynamic_upstream_op_add_impl(ngx_log_t * log,ngx_dynamic_upstream_op_t * op,ngx_slab_pool_t * shpool,ngx_pool_t * temp_pool,typename TypeSelect<S>::peers_type * primary,ngx_url_t * u)377 ngx_dynamic_upstream_op_add_impl(ngx_log_t *log,
378     ngx_dynamic_upstream_op_t *op, ngx_slab_pool_t *shpool,
379     ngx_pool_t *temp_pool, typename TypeSelect<S>::peers_type *primary,
380     ngx_url_t *u)
381 {
382     unsigned                   j;
383     unsigned                   count = 0;
384     ngx_flag_t                 empty;
385     ngx_dynamic_upstream_op_t  del_op;
386 
387     ngx_upstream_peers_wlock<typename TypeSelect<S>::peers_type>
388         lock(primary, op->no_lock);
389 
390     empty = primary->single && is_reserved_addr(&primary->peer->server);
391 
392     for (j = 0; j < u->naddrs; j++) {
393 
394         if (ngx_dynamic_upstream_op_add_peer<S>(log, op, shpool, primary, u, j)
395                 == NGX_ERROR) {
396             return NGX_ERROR;
397         }
398 
399         if (op->status == NGX_HTTP_OK)
400             count++;
401     }
402 
403     if (empty && !primary->single) {
404 
405         ngx_memzero(&del_op, sizeof(ngx_dynamic_upstream_op_t));
406 
407         del_op.no_lock = 1;
408         del_op.op = NGX_DYNAMIC_UPSTEAM_OP_REMOVE;
409         del_op.upstream = op->upstream;
410         del_op.server = noaddr;
411         del_op.name = noaddr;
412 
413         ngx_dynamic_upstream_op_del<S>(primary, &del_op, shpool,
414             temp_pool, log);
415     }
416 
417     op->status = count != 0 ? NGX_HTTP_OK : NGX_HTTP_NOT_MODIFIED;
418 
419     return NGX_OK;
420 }
421 
422 
423 template <class S> static ngx_int_t
ngx_dynamic_upstream_op_add(typename TypeSelect<S>::peers_type * primary,ngx_dynamic_upstream_op_t * op,ngx_slab_pool_t * shpool,ngx_pool_t * temp_pool,ngx_log_t * log)424 ngx_dynamic_upstream_op_add(typename TypeSelect<S>::peers_type *primary,
425     ngx_dynamic_upstream_op_t *op, ngx_slab_pool_t *shpool,
426     ngx_pool_t *temp_pool, ngx_log_t *log)
427 {
428     ngx_url_t  u;
429     ngx_int_t  rc;
430 
431     if ((rc = ngx_dynamic_upstream_parse_url(&u, temp_pool, op)) == NGX_ERROR)
432         return NGX_ERROR;
433 
434     if (rc == NGX_AGAIN) {
435 
436         if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_RESOLVE) {
437 
438             op->down = 1;
439             op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_DOWN;
440 
441         } else {
442 
443             op->status = NGX_HTTP_BAD_REQUEST;
444             op->err = "domain names are supported only for upstreams "
445                       "with 'dns_update' directive";
446 
447             return NGX_ERROR;
448         }
449     }
450 
451     if (ngx_dynamic_upstream_op_add_impl<S>(log, op, shpool, temp_pool,
452                                             primary, &u) == NGX_ERROR)
453         return NGX_ERROR;
454 
455     if (op->status == NGX_HTTP_NOT_MODIFIED)
456         return NGX_OK;
457 
458     if (rc == NGX_AGAIN) {
459 
460         op->status = NGX_HTTP_PROCESSING;
461         op->err = "DNS resolving in progress";
462     }
463 
464     return rc;
465 }
466 
467 
468 struct ngx_server_s {
469     ngx_str_t name;
470     ngx_int_t backup;
471     ngx_int_t weight;
472     ngx_int_t max_fails;
473 
474 #if defined(nginx_version) && (nginx_version >= 1011005)
475     ngx_int_t max_conns;
476 #endif
477 
478     ngx_int_t fail_timeout;
479     ngx_url_t u;
480 };
481 typedef struct ngx_server_s ngx_server_t;
482 
483 
484 static ngx_uint_t
ngx_dynamic_upstream_op_server_exist(ngx_array_t * servers,ngx_str_t name)485 ngx_dynamic_upstream_op_server_exist(ngx_array_t *servers,
486     ngx_str_t name)
487 {
488     ngx_server_t  *server = (ngx_server_t *) servers->elts;
489     unsigned       j;
490 
491     for (j = 0; j < servers->nelts; j++)
492         if (str_eq(server[j].name, name))
493             return 1;
494 
495     return 0;
496 }
497 
498 
499 template <class S> ngx_uint_t
ngx_dynamic_upstream_op_peer_exist(ngx_array_t * servers,typename TypeSelect<S>::peer_type * peer)500 ngx_dynamic_upstream_op_peer_exist(ngx_array_t *servers,
501     typename TypeSelect<S>::peer_type *peer)
502 {
503     ngx_server_t  *server = (ngx_server_t *) servers->elts;
504     unsigned       i, j;
505 
506     for (j = 0; j < servers->nelts; j++) {
507 
508         if (!str_eq(server[j].name, peer->server))
509             continue;
510 
511         if (server[j].u.naddrs == 0)
512             return 1;
513 
514         for (i = 0; i < server[j].u.naddrs; i++) {
515 
516             if (str_eq(server[j].u.addrs[i].name, peer->name))
517                 return 1;
518         }
519     }
520 
521     return 0;
522 }
523 
524 
525 template <class S> static ngx_int_t
ngx_dynamic_upstream_op_servers(typename TypeSelect<S>::peers_type * primary,ngx_array_t * servers,ngx_pool_t * pool,ngx_uint_t * hash)526 ngx_dynamic_upstream_op_servers(typename TypeSelect<S>::peers_type *primary,
527     ngx_array_t *servers, ngx_pool_t *pool, ngx_uint_t *hash)
528 {
529     typename TypeSelect<S>::peers_type  *peers;
530     typename TypeSelect<S>::peer_type   *peer;
531 
532     ngx_server_t  *server;
533     ngx_uint_t     j = 0;
534 
535     *hash = 0;
536 
537     ngx_upstream_peers_rlock<typename TypeSelect<S>::peers_type> lock(primary);
538 
539     for (peers = primary;
540          peers != NULL && j < 2;
541          peers = peers->next, j++) {
542 
543         for (peer = peers->peer;
544              peer != NULL;
545              peer = peer->next) {
546 
547             if (!ngx_dynamic_upstream_op_server_exist(servers, peer->server)) {
548 
549                 server = (ngx_server_t *) ngx_array_push(servers);
550                 if (server == NULL)
551                     return NGX_ERROR;
552 
553                 server->name.data = (u_char *) ngx_pcalloc(pool,
554                     peer->server.len + 1);
555                 if (server->name.data == NULL)
556                     return NGX_ERROR;
557 
558                 ngx_memcpy(server->name.data, peer->server.data,
559                            peer->server.len);
560                 server->name.len     = peer->server.len;
561                 server->backup       = j == 1;
562                 server->weight       = peer->weight;
563                 server->max_fails    = peer->max_fails;
564                 server->fail_timeout = peer->fail_timeout;
565 #if defined(nginx_version) && (nginx_version >= 1011005)
566                 server->max_conns    = peer->max_conns;
567 #endif
568             }
569 
570             *hash += ngx_crc32_short(peer->server.data, peer->server.len) << j;
571         }
572     }
573 
574     return NGX_OK;
575 }
576 
577 
578 template <class S> static ngx_int_t
ngx_dynamic_upstream_op_check_hash(typename TypeSelect<S>::peers_type * primary,ngx_uint_t * hash)579 ngx_dynamic_upstream_op_check_hash(typename TypeSelect<S>::peers_type *primary,
580     ngx_uint_t *hash)
581 {
582     typename TypeSelect<S>::peers_type  *peers;
583     typename TypeSelect<S>::peer_type   *peer;
584 
585     ngx_uint_t  j;
586     ngx_uint_t  old_hash = *hash;
587 
588     *hash = 0;
589 
590     for (peers = primary, j = 0;
591          peers != NULL && j < 2;
592          peers = peers->next, j++)
593 
594         for (peer = peers->peer;
595              peer != NULL;
596              peer = peer->next)
597 
598             *hash += ngx_crc32_short(peer->server.data, peer->server.len) << j;
599 
600     return *hash == old_hash ? NGX_OK : NGX_DECLINED;
601 }
602 
603 
604 template <class S> static ngx_int_t
ngx_dynamic_upstream_op_hash(typename TypeSelect<S>::peers_type * primary,ngx_dynamic_upstream_op_t * op)605 ngx_dynamic_upstream_op_hash(typename TypeSelect<S>::peers_type *primary,
606     ngx_dynamic_upstream_op_t *op)
607 {
608     ngx_upstream_peers_rlock<typename TypeSelect<S>::peers_type> lock(primary);
609     return ngx_dynamic_upstream_op_check_hash<S>(primary,
610         &op->hash);
611 }
612 
613 
614 template <class S> static ngx_int_t
ngx_dynamic_upstream_op_sync(typename TypeSelect<S>::peers_type * primary,ngx_dynamic_upstream_op_t * op,ngx_slab_pool_t * shpool,ngx_pool_t * temp_pool,ngx_log_t * log)615 ngx_dynamic_upstream_op_sync(typename TypeSelect<S>::peers_type *primary,
616     ngx_dynamic_upstream_op_t *op, ngx_slab_pool_t *shpool,
617     ngx_pool_t *temp_pool, ngx_log_t *log)
618 {
619     typename TypeSelect<S>::peers_type  *peers;
620     typename TypeSelect<S>::peer_type   *peer;
621 
622     unsigned       i, j;
623     ngx_server_t  *server;
624     unsigned       count = 0;
625     ngx_array_t   *servers;
626     ngx_uint_t     hash = op->hash;
627 
628     if (ngx_dynamic_upstream_op_hash<S>(primary, op) == NGX_OK) {
629 
630         op->status = NGX_HTTP_NOT_MODIFIED;
631 
632         return NGX_OK;
633     }
634 
635     op->hash = hash;
636 
637     servers = ngx_array_create(temp_pool, 100, sizeof(ngx_server_t));
638     if (servers == NULL) {
639 
640         op->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
641         op->err = "no memory";
642 
643         return NGX_ERROR;
644     }
645 
646 again:
647 
648     if (ngx_dynamic_upstream_op_servers<S>(primary,
649                                            servers,
650                                            temp_pool,
651                                            &hash)
652             == NGX_ERROR)
653     {
654         op->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
655         op->err = "no memory";
656         return NGX_ERROR;
657     }
658 
659     server = (ngx_server_t *) servers->elts;
660 
661     for (j = 0; j < servers->nelts; j++) {
662 
663         op->server = server[j].name;
664 
665         if (ngx_dynamic_upstream_parse_url(&server[j].u, temp_pool,
666                                            op) != NGX_OK) {
667 
668             ngx_log_error(NGX_LOG_WARN, log, 0, "%V: server %V: %s",
669                           &op->upstream, &op->server, op->err);
670 
671             op->status = NGX_HTTP_OK;
672             op->err = NULL;
673         }
674     }
675 
676     ngx_upstream_peers_wlock<typename TypeSelect<S>::peers_type> lock(primary);
677 
678     if (ngx_dynamic_upstream_op_check_hash<S>(primary, &hash) == NGX_DECLINED) {
679 
680         servers->nelts = 0;
681         goto again;
682     }
683 
684     op->no_lock = 1;
685 
686     op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_WEIGHT;
687     op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_MAX_FAILS;
688     op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_FAIL_TIMEOUT;
689 #if defined(nginx_version) && (nginx_version >= 1011005)
690     op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_MAX_CONNS;
691 #endif
692 
693     for (j = 0; j < servers->nelts; j++) {
694 
695         op->server       = server[j].name;
696         op->weight       = server[j].weight;
697         op->backup       = server[j].backup;
698         op->max_fails    = server[j].max_fails;
699 #if defined(nginx_version) && (nginx_version >= 1011005)
700         op->max_conns    = server[j].max_conns;
701 #endif
702         op->fail_timeout = server[j].fail_timeout;
703 
704         for (i = 0; i < server[j].u.naddrs; i++) {
705 
706             if (str_eq(op->server, server[j].u.addrs[i].name))
707                 break;
708 
709             if (ngx_dynamic_upstream_op_add_peer<S>
710                     (log, op, shpool, primary, &server[j].u, i) == NGX_ERROR)
711                 return NGX_ERROR;
712 
713             if (op->status == NGX_HTTP_OK)
714                 count++;
715         }
716     }
717 
718     for (peers = primary, j = 0;
719          peers != NULL && j < 2;
720          peers = peers->next, j++) {
721 
722         for (peer = peers->peer;
723              peer != NULL;
724              peer = peer->next) {
725 
726             if ((peer->name.data[0] == '['
727                  && !(op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_IPV6))
728                 || !ngx_dynamic_upstream_op_peer_exist<S>(servers, peer)) {
729 
730                 op->server = peer->server;
731                 op->name = peer->name;
732 
733                 if (ngx_dynamic_upstream_op_del<S>(primary, op, shpool,
734                                                    temp_pool, log) == NGX_ERROR)
735                     return NGX_ERROR;
736 
737                 count++;
738             }
739         }
740     }
741 
742     op->hash = hash;
743     op->status = count != 0 ? NGX_HTTP_OK : NGX_HTTP_NOT_MODIFIED;
744 
745     return NGX_OK;
746 }
747 
748 
749 typedef ngx_int_t (*cleanup_t) (ngx_slab_pool_t *shpool, void *peer);
750 
751 
752 typedef struct {
753     ngx_slab_pool_t  *shpool;
754     void             *peer;
755     cleanup_t         free;
756 } ngx_dynamic_cleanup_t;
757 
758 
759 static void
760 gc_collect(ngx_event_t *ev);
761 
762 
763 static ngx_array_t *gc = NULL;
764 
765 
766 static ngx_array_t *
ngx_dynamic_gc_init()767 ngx_dynamic_gc_init()
768 {
769     ngx_pool_t  *pool = ngx_create_pool(1024, ngx_cycle->log);
770     if (pool == NULL)
771         return NULL;
772 
773     gc = ngx_array_create(pool, 100, sizeof(ngx_dynamic_cleanup_t));
774     if (gc == NULL)
775         return NULL;
776 
777     static ngx_connection_t  c;
778     static ngx_event_t       ev;
779 
780     c.fd = -1;
781     ev.handler = gc_collect;
782     ev.data = &c;
783     ev.log = ngx_cycle->log;
784 
785     ngx_add_timer(&ev, 1000);
786 
787     return gc;
788 }
789 
790 
791 static void
ngx_dynamic_add_to_gc(ngx_slab_pool_t * shpool,void * peer,cleanup_t cb)792 ngx_dynamic_add_to_gc(ngx_slab_pool_t *shpool, void *peer, cleanup_t cb)
793 {
794     ngx_dynamic_cleanup_t  *p;
795 
796     if (gc == NULL && ngx_dynamic_gc_init() == NULL)
797         return;
798 
799     p = (ngx_dynamic_cleanup_t *) ngx_array_push(gc);
800     if (p != NULL) {
801         p->shpool = shpool;
802         p->peer = peer;
803         p->free = cb;
804     }
805 }
806 
807 
808 static void
gc_collect(ngx_event_t * ev)809 gc_collect(ngx_event_t *ev)
810 {
811     ngx_dynamic_cleanup_t  *elts = (ngx_dynamic_cleanup_t *) gc->elts;
812     ngx_uint_t              i, j = 0;
813 
814     if (gc->nelts == 0)
815         goto settimer;
816 
817     for (i = 0; i < gc->nelts; i++)
818         if (elts[i].free(elts[i].shpool, elts[i].peer) == NGX_AGAIN)
819             elts[j++] = elts[i];
820 
821     gc->nelts = j;
822 
823 settimer:
824 
825     if (ngx_exiting || ngx_terminate || ngx_quit)
826         return;
827 
828     ngx_add_timer(ev, 1000);
829 }
830 
831 
832 template <class PeerT> struct GC
833 {
collectGC834     static ngx_int_t collect(ngx_slab_pool_t *shpool, void *p)
835     {
836         PeerT *peer = static_cast<PeerT *>(p);
837 
838         ngx_upstream_peer_rlock<PeerT> lock(peer);
839 
840         if (peer->conns == 0) {
841             ngx_slab_free(shpool, peer->server.data);
842             ngx_slab_free(shpool, peer->name.data);
843             ngx_slab_free(shpool, peer->sockaddr);
844 
845             lock.release();
846 
847             ngx_slab_free(shpool, peer);
848 
849             return NGX_OK;
850         }
851 
852         return NGX_AGAIN;
853     }
854 };
855 
856 
857 template <class PeerT> static void
ngx_dynamic_upstream_op_free_peer(ngx_slab_pool_t * shpool,PeerT * peer)858 ngx_dynamic_upstream_op_free_peer(ngx_slab_pool_t *shpool, PeerT *peer)
859 {
860     if (GC<PeerT>::collect(shpool, peer) == NGX_AGAIN)
861         ngx_dynamic_add_to_gc(shpool, peer, &GC<PeerT>::collect);
862 }
863 
864 
865 template <class S> static ngx_int_t
ngx_dynamic_upstream_op_del(typename TypeSelect<S>::peers_type * primary,ngx_dynamic_upstream_op_t * op,ngx_slab_pool_t * shpool,ngx_pool_t * temp_pool,ngx_log_t * log)866 ngx_dynamic_upstream_op_del(typename TypeSelect<S>::peers_type *primary,
867     ngx_dynamic_upstream_op_t *op, ngx_slab_pool_t *shpool,
868     ngx_pool_t *temp_pool, ngx_log_t *log)
869 {
870     SearchResult<S>            found;
871     ngx_dynamic_upstream_op_t  add_op;
872 
873     op->status = NGX_HTTP_NOT_MODIFIED;
874 
875     ngx_upstream_peers_wlock<typename TypeSelect<S>::peers_type> lock(primary,
876         op->no_lock);
877 
878 again:
879 
880     found = search_peer<S>(primary, op->server, op->name, 1);
881     if (found.peer == NULL)
882         return NGX_OK;
883 
884     if (!found.peers->single || found.peers == primary->next)
885         /* not single or backup */
886         goto check;
887 
888     if (equals<typename TypeSelect<S>::peer_type>(found.peer, noaddr, noaddr))
889         return NGX_OK;
890 
891     ngx_memzero(&add_op, sizeof(ngx_dynamic_upstream_op_t));
892 
893     add_op.no_lock = 1;
894     add_op.op = NGX_DYNAMIC_UPSTEAM_OP_ADD;
895     add_op.upstream = op->upstream;
896     add_op.server = noaddr;
897     add_op.name = noaddr;
898     add_op.op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_DOWN;
899     add_op.down = 1;
900 
901     if (ngx_dynamic_upstream_op_add<S>(primary, &add_op,
902             shpool, temp_pool, log) != NGX_OK) {
903         op->err = add_op.err;
904         op->status = add_op.status;
905         return NGX_ERROR;
906     }
907 
908     goto again;
909 
910 check:
911 
912     if (found.prev == NULL) {
913         /* found head */
914         found.peers->peer = found.peer->next;
915         goto del;
916     }
917 
918     if (found.peer->next == NULL) {
919         /* found tail */
920         found.prev->next = NULL;
921         goto del;
922     }
923 
924     /* found inside */
925     found.prev->next = found.peer->next;
926 
927  del:
928 
929     found.peers->number--;
930     found.peers->total_weight -= found.peer->weight;
931     found.peers->single = found.peers->number == 1;
932     found.peers->weighted = found.peers->total_weight != found.peers->number;
933 
934     if (found.peers->number == 0) {
935         assert(found.peers == primary->next);
936         ngx_slab_free(shpool, primary->next);
937         primary->next = NULL;
938     }
939 
940     if (!is_reserved_addr(&found.peer->name))
941         ngx_log_error(NGX_LOG_NOTICE, log, 0, "%V: removed server %V peer %V",
942                       &op->upstream, &found.peer->server, &found.peer->name);
943 
944     ngx_dynamic_upstream_op_free_peer<typename TypeSelect<S>::peer_type>(shpool,
945         found.peer);
946 
947     op->status = NGX_HTTP_OK;
948 
949     goto again;
950 }
951 
952 
953 template <class S> static void
ngx_dynamic_upstream_op_update_peer(typename TypeSelect<S>::peers_type * peers,typename TypeSelect<S>::peer_type * peer,ngx_dynamic_upstream_op_t * op,ngx_log_t * log)954 ngx_dynamic_upstream_op_update_peer(typename TypeSelect<S>::peers_type *peers,
955     typename TypeSelect<S>::peer_type *peer,
956     ngx_dynamic_upstream_op_t *op, ngx_log_t *log)
957 {
958     ngx_upstream_peer_wlock<typename TypeSelect<S>::peer_type> lock(peer);
959 
960     if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_WEIGHT) {
961 
962         peers->total_weight -= peer->weight;
963         peers->total_weight += op->weight;
964         peers->weighted = peers->total_weight != peers->number;
965         peer->weight = op->weight;
966         peer->current_weight = op->weight;
967         peer->effective_weight = op->weight;
968 
969     }
970 
971     if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_MAX_FAILS)
972         peer->max_fails = op->max_fails;
973 
974 #if defined(nginx_version) && (nginx_version >= 1011005)
975     if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_MAX_CONNS)
976         peer->max_conns = op->max_conns;
977 #endif
978 
979     if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_FAIL_TIMEOUT)
980         peer->fail_timeout = op->fail_timeout;
981 
982     if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_UP) {
983 
984         peer->down = 0;
985         peer->checked = ngx_time();
986         peer->fails = 0;
987 
988         ngx_log_error(NGX_LOG_NOTICE, log, 0, "%V: up peer %V",
989                       &op->upstream, &peer->name);
990     }
991 
992     if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_DOWN) {
993 
994         peer->down = 1;
995         peer->checked = ngx_time();
996         peer->fails = peer->max_fails;
997 
998         ngx_log_error(NGX_LOG_NOTICE, log, 0, "%V: down peer %V",
999                       &op->upstream, &peer->name);
1000     }
1001 }
1002 
1003 
1004 template <class S> static ngx_int_t
ngx_dynamic_upstream_op_update(typename TypeSelect<S>::peers_type * primary,ngx_dynamic_upstream_op_t * op,ngx_log_t * log)1005 ngx_dynamic_upstream_op_update(typename TypeSelect<S>::peers_type *primary,
1006     ngx_dynamic_upstream_op_t *op, ngx_log_t *log)
1007 {
1008     SearchResult<S>  found;
1009 
1010     ngx_upstream_peers_rlock<typename TypeSelect<S>::peers_type> lock(primary,
1011         op->no_lock);
1012 
1013     found = search_peer<S>(primary, op->server, op->name, 1);
1014     if (found.peer == NULL) {
1015         op->status = NGX_HTTP_BAD_REQUEST;
1016         op->err = "server or peer is not found";
1017         return NGX_ERROR;
1018     }
1019 
1020     ngx_dynamic_upstream_op_update_peer<S>(found.peers, found.peer, op, log);
1021 
1022     return NGX_OK;
1023 }
1024