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