diff --git a/README.md b/README.md index 7ddcb1b..93657fa 100644 --- a/README.md +++ b/README.md @@ -43,13 +43,13 @@ http { listen 6000; location /dynamic { - allow 127.0.0.1; + allow 127.0.0.1; deny all; dynamic_upstream; } location / { - proxy_pass http://backends; + proxy_pass http://backends; } } } @@ -87,19 +87,19 @@ $ ```bash $ curl "http://127.0.0.1:6000/dynamic?upstream=zone_for_backends&verbose=" -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; $ ``` ## update_parameters ```bash -$ curl "http://127.0.0.1:6000/dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&weight=10&max_fails=5&fail_timeout=5" -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=10 max_fails=5 fail_timeout=5; +$ curl "http://127.0.0.1:6000/dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&weight=10&max_fails=5&fail_timeout=5&max_conns=10" +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=10 max_fails=5 fail_timeout=5 max_conns=10 conns=0; $ ``` @@ -113,9 +113,9 @@ The supported parameters are below. ```bash $ curl "http://127.0.0.1:6000/dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&down=" -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10 down; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0 down; $ ``` @@ -123,9 +123,9 @@ $ ```bash $ curl "http://127.0.0.1:6000/dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&up=" -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; $ ``` diff --git a/config b/config index 7a9d582..efce6ac 100644 --- a/config +++ b/config @@ -1,9 +1,9 @@ -ngx_addon_name=ngx_dynamic_upstream_module +ngx_addon_name="ngx_http_dynamic_upstream_module" -DYNAMIC_UPSTREAM_SRCS=" \ - $ngx_addon_dir/src/ngx_dynamic_upstream_module.c \ - $ngx_addon_dir/src/ngx_dynamic_upstream_op.c \ - $ngx_addon_dir/src/ngx_inet_slab.c \ +DYNAMIC_UPSTREAM_SRCS=" \ + $ngx_addon_dir/src/ngx_http_dynamic_upstream_module.c \ + $ngx_addon_dir/src/ngx_dynamic_upstream_op.c \ + $ngx_addon_dir/src/ngx_inet_slab.c \ " DYNAMIC_UPSTREAM_DEPS=" \ diff --git a/src/ngx_dynamic_upstream_op.c b/src/ngx_dynamic_upstream_op.c index eaad765..ad63d0a 100644 --- a/src/ngx_dynamic_upstream_op.c +++ b/src/ngx_dynamic_upstream_op.c @@ -1,4 +1,3 @@ - #include #include #include @@ -33,26 +32,44 @@ static const ngx_str_t ngx_dynamic_upstream_params[] = { }; +void *DELETED = (void *) 0xDEADDEAD; + + static ngx_int_t ngx_dynamic_upstream_is_shpool_range(ngx_slab_pool_t *shpool, void *p); + + static ngx_int_t ngx_dynamic_upstream_http_op_add(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, - ngx_slab_pool_t *shpool, ngx_http_upstream_rr_peers_t *primary); + ngx_slab_pool_t *shpool, ngx_http_upstream_rr_peers_t *primary); + + static ngx_int_t -ngx_dynamic_upstream_stream_op_add(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, - ngx_slab_pool_t *shpool, ngx_stream_upstream_rr_peers_t *primary); +ngx_dynamic_upstream_stream_op_add(ngx_log_t *log, + ngx_dynamic_upstream_op_t *op, ngx_slab_pool_t *shpool, + ngx_stream_upstream_rr_peers_t *primary); + + static ngx_int_t -ngx_dynamic_upstream_http_op_remove(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, - ngx_slab_pool_t *shpool, ngx_http_upstream_rr_peers_t *primary); +ngx_dynamic_upstream_http_op_del(ngx_log_t *log, + ngx_dynamic_upstream_op_t *op, ngx_slab_pool_t *shpool, + ngx_http_upstream_rr_peers_t *primary); + + static ngx_int_t -ngx_dynamic_upstream_stream_op_remove(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, - ngx_slab_pool_t *shpool, ngx_stream_upstream_rr_peers_t *primary); +ngx_dynamic_upstream_stream_op_del(ngx_log_t *log, + ngx_dynamic_upstream_op_t *op, ngx_slab_pool_t *shpool, + ngx_stream_upstream_rr_peers_t *primary); + + static ngx_int_t -ngx_dynamic_upstream_http_op_update_param(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, - ngx_http_upstream_rr_peers_t *primary); +ngx_dynamic_upstream_http_op_update(ngx_log_t *log, + ngx_dynamic_upstream_op_t *op, ngx_http_upstream_rr_peers_t *primary); + + static ngx_int_t -ngx_dynamic_upstream_stream_op_update_param(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, - ngx_stream_upstream_rr_peers_t *primary); +ngx_dynamic_upstream_stream_op_update(ngx_log_t *log, + ngx_dynamic_upstream_op_t *op, ngx_stream_upstream_rr_peers_t *primary); static ngx_int_t @@ -67,7 +84,8 @@ ngx_dynamic_upstream_is_shpool_range(ngx_slab_pool_t *shpool, void *p) ngx_int_t -ngx_dynamic_upstream_build_op(ngx_http_request_t *r, ngx_dynamic_upstream_op_t *op) +ngx_dynamic_upstream_build_op(ngx_http_request_t *r, + ngx_dynamic_upstream_op_t *op) { ngx_uint_t i; size_t args_size; @@ -92,10 +110,7 @@ ngx_dynamic_upstream_build_op(ngx_http_request_t *r, ngx_dynamic_upstream_op_t * low = ngx_pnalloc(r->pool, args[i].len); if (low == NULL) { op->status = NGX_HTTP_INTERNAL_SERVER_ERROR; - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "failed to allocate memory from r->pool %s:%d", - __FUNCTION__, - __LINE__); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no memory"); return NGX_ERROR; } @@ -128,9 +143,7 @@ ngx_dynamic_upstream_build_op(ngx_http_request_t *r, ngx_dynamic_upstream_op_t * if (op->weight == NGX_ERROR) { op->status = NGX_HTTP_BAD_REQUEST; ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "weight is not number. %s:%d", - __FUNCTION__, - __LINE__); + "weight is not number"); return NGX_ERROR; } op->op |= NGX_DYNAMIC_UPSTEAM_OP_PARAM; @@ -142,9 +155,7 @@ ngx_dynamic_upstream_build_op(ngx_http_request_t *r, ngx_dynamic_upstream_op_t * if (op->max_fails == NGX_ERROR) { op->status = NGX_HTTP_BAD_REQUEST; ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "max_fails is not number. %s:%d", - __FUNCTION__, - __LINE__); + "max_fails is not number"); return NGX_ERROR; } op->op |= NGX_DYNAMIC_UPSTEAM_OP_PARAM; @@ -158,9 +169,7 @@ ngx_dynamic_upstream_build_op(ngx_http_request_t *r, ngx_dynamic_upstream_op_t * if (op->max_conns == NGX_ERROR) { op->status = NGX_HTTP_BAD_REQUEST; ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "max_conns is not number. %s:%d", - __FUNCTION__, - __LINE__); + "max_conns is not number"); return NGX_ERROR; } op->op |= NGX_DYNAMIC_UPSTEAM_OP_PARAM; @@ -173,9 +182,7 @@ ngx_dynamic_upstream_build_op(ngx_http_request_t *r, ngx_dynamic_upstream_op_t * if (op->fail_timeout == NGX_ERROR) { op->status = NGX_HTTP_BAD_REQUEST; ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "fail_timeout is not number. %s:%d", - __FUNCTION__, - __LINE__); + "fail_timeout is not number"); return NGX_ERROR; } op->op |= NGX_DYNAMIC_UPSTEAM_OP_PARAM; @@ -207,9 +214,7 @@ ngx_dynamic_upstream_build_op(ngx_http_request_t *r, ngx_dynamic_upstream_op_t * { op->status = NGX_HTTP_BAD_REQUEST; ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "add and remove at once are not allowed. %s:%d", - __FUNCTION__, - __LINE__); + "add and remove at once are not allowed"); return NGX_ERROR; } @@ -223,9 +228,7 @@ ngx_dynamic_upstream_build_op(ngx_http_request_t *r, ngx_dynamic_upstream_op_t * if (op->up && op->down) { op->status = NGX_HTTP_BAD_REQUEST; ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "down and up at once are not allowed. %s:%d", - __FUNCTION__, - __LINE__); + "down and up at once are not allowed"); return NGX_ERROR; } @@ -235,34 +238,35 @@ ngx_dynamic_upstream_build_op(ngx_http_request_t *r, ngx_dynamic_upstream_op_t * ngx_int_t ngx_dynamic_upstream_op_impl(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, - ngx_slab_pool_t *shpool, ngx_upstream_rr_peers_t *primary) + ngx_slab_pool_t *shpool, ngx_upstream_rr_peers_t *peers) { - ngx_int_t rc; - - rc = NGX_OK; + ngx_int_t rc = NGX_OK; if (shpool) { ngx_shmtx_lock(&shpool->mutex); } if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_STREAM) { - ngx_http_upstream_rr_peers_wlock(primary->stream); + ngx_http_upstream_rr_peers_wlock(peers->stream); } else { - ngx_http_upstream_rr_peers_wlock(primary->http); + ngx_http_upstream_rr_peers_wlock(peers->http); } switch (op->op) { case NGX_DYNAMIC_UPSTEAM_OP_ADD: - rc = op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_STREAM ? ngx_dynamic_upstream_stream_op_add(log, op, shpool, primary->stream) - : ngx_dynamic_upstream_http_op_add(log, op, shpool, primary->http); + rc = op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_STREAM + ? ngx_dynamic_upstream_stream_op_add(log, op, shpool, peers->stream) + : ngx_dynamic_upstream_http_op_add(log, op, shpool, peers->http); break; case NGX_DYNAMIC_UPSTEAM_OP_REMOVE: - rc = op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_STREAM ? ngx_dynamic_upstream_stream_op_remove(log, op, shpool, primary->stream) - : ngx_dynamic_upstream_http_op_remove(log, op, shpool, primary->http); + rc = op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_STREAM + ? ngx_dynamic_upstream_stream_op_del(log, op, shpool, peers->stream) + : ngx_dynamic_upstream_http_op_del(log, op, shpool, peers->http); break; case NGX_DYNAMIC_UPSTEAM_OP_PARAM: - rc = op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_STREAM ? ngx_dynamic_upstream_stream_op_update_param(log, op, primary->stream) - : ngx_dynamic_upstream_http_op_update_param(log, op, primary->http); + rc = op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_STREAM + ? ngx_dynamic_upstream_stream_op_update(log, op, peers->stream) + : ngx_dynamic_upstream_http_op_update(log, op, peers->http); break; case NGX_DYNAMIC_UPSTEAM_OP_LIST: default: @@ -271,9 +275,9 @@ ngx_dynamic_upstream_op_impl(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, } if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_STREAM) { - ngx_http_upstream_rr_peers_unlock(primary->stream); + ngx_http_upstream_rr_peers_unlock(peers->stream); } else { - ngx_http_upstream_rr_peers_unlock(primary->http); + ngx_http_upstream_rr_peers_unlock(peers->http); } if (shpool) { @@ -285,27 +289,36 @@ ngx_dynamic_upstream_op_impl(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, static ngx_int_t -ngx_dynamic_upstream_http_op_add(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, - ngx_slab_pool_t *shpool, ngx_http_upstream_rr_peers_t *primary) +ngx_dynamic_upstream_http_op_add(ngx_log_t *log, + ngx_dynamic_upstream_op_t *op, ngx_slab_pool_t *shpool, + ngx_http_upstream_rr_peers_t *primary) { ngx_http_upstream_rr_peer_t *peer, *last = NULL, *new_peer; ngx_http_upstream_rr_peers_t *peers, *backup; ngx_url_t u; + if (shpool == NULL) { + op->status = NGX_HTTP_NOT_IMPLEMENTED; + ngx_log_error(NGX_LOG_ERR, log, 0, + "add is possible only for upstream with 'zone'"); + return NGX_ERROR; + } + backup = primary->next; for (peers = primary; peers; peers = peers->next) { for (peer = peers->peer; peer; peer = peer->next) { - if (op->server.len == peer->name.len && ngx_strncmp(op->server.data, peer->name.data, peer->name.len) == 0) { + if (op->server.len == peer->name.len && + ngx_strncmp(op->server.data, peer->name.data, + peer->name.len) == 0) { op->status = NGX_HTTP_BAD_REQUEST; ngx_log_error(NGX_LOG_ERR, log, 0, - "server %V already exists in upstream. %s:%d", - &op->server, - __FUNCTION__, - __LINE__); + "server %V already exists in upstream", + &op->server); return NGX_ERROR; } - if ( (op->backup == 0 && peers == primary) || (op->backup == 1 && peers == backup) ) { + if ( (op->backup == 0 && peers == primary) || + (op->backup == 1 && peers == backup) ) { last = peer; } } @@ -314,13 +327,11 @@ ngx_dynamic_upstream_http_op_add(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, if (op->backup) { if (backup == NULL) { assert(last == NULL); - backup = ngx_slab_calloc_locked(shpool, sizeof(ngx_http_upstream_rr_peers_t)); + backup = ngx_slab_calloc_locked(shpool, + sizeof(ngx_http_upstream_rr_peers_t)); if (backup == NULL) { op->status = NGX_HTTP_INTERNAL_SERVER_ERROR; - ngx_log_error(NGX_LOG_ERR, log, 0, - "failed to allocate memory from slab %s:%d", - __FUNCTION__, - __LINE__); + ngx_log_error(NGX_LOG_ERR, log, 0, "no shared memory"); return NGX_ERROR; } backup->shpool = primary->shpool; @@ -334,20 +345,18 @@ ngx_dynamic_upstream_http_op_add(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, ngx_memzero(&u, sizeof(ngx_url_t)); - u.url.data = ngx_slab_alloc_locked(shpool, op->server.len); + u.url.data = ngx_slab_calloc_locked(shpool, op->server.len + 1); if (u.url.data == NULL) { if (backup && primary->next == NULL) { ngx_slab_free_locked(shpool, backup); } op->status = NGX_HTTP_INTERNAL_SERVER_ERROR; - ngx_log_error(NGX_LOG_ERR, log, 0, - "failed to allocate memory from slab %s:%d", - __FUNCTION__, - __LINE__); + ngx_log_error(NGX_LOG_ERR, log, 0, "no shared memory"); return NGX_ERROR; } - ngx_cpystrn(u.url.data, op->server.data, op->server.len + 1); - u.url.len = op->server.len; + + ngx_memcpy(u.url.data, op->server.data, op->server.len); + u.url.len = op->server.len; u.default_port = 80; if (ngx_parse_url_slab(shpool, &u) != NGX_OK) { @@ -362,16 +371,14 @@ ngx_dynamic_upstream_http_op_add(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, return NGX_ERROR; } - new_peer = ngx_slab_calloc_locked(shpool, sizeof(ngx_http_upstream_rr_peer_t)); + new_peer = ngx_slab_calloc_locked(shpool, + sizeof(ngx_http_upstream_rr_peer_t)); if (new_peer == NULL) { if (backup && primary->next == NULL) { ngx_slab_free_locked(shpool, backup); } op->status = NGX_HTTP_INTERNAL_SERVER_ERROR; - ngx_log_error(NGX_LOG_ERR, log, 0, - "failed to allocate memory from slab %s:%d", - __FUNCTION__, - __LINE__); + ngx_log_error(NGX_LOG_ERR, log, 0, "no shared memory"); return NGX_ERROR; } @@ -421,35 +428,43 @@ ngx_dynamic_upstream_http_op_add(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, primary->next = backup; } - ngx_log_error(NGX_LOG_NOTICE, log, 0, - "added server %V", &op->server); + ngx_log_error(NGX_LOG_NOTICE, log, 0, "added server %V", &op->server); return NGX_OK; } static ngx_int_t -ngx_dynamic_upstream_stream_op_add(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, - ngx_slab_pool_t *shpool, ngx_stream_upstream_rr_peers_t *primary) +ngx_dynamic_upstream_stream_op_add(ngx_log_t *log, + ngx_dynamic_upstream_op_t *op, ngx_slab_pool_t *shpool, + ngx_stream_upstream_rr_peers_t *primary) { ngx_stream_upstream_rr_peer_t *peer, *last = NULL, *new_peer; ngx_stream_upstream_rr_peers_t *peers, *backup; ngx_url_t u; + if (shpool == NULL) { + op->status = NGX_HTTP_NOT_IMPLEMENTED; + ngx_log_error(NGX_LOG_ERR, log, 0, + "add is possible only for upstream with 'zone'"); + return NGX_ERROR; + } + backup = primary->next; for (peers = primary; peers; peers = peers->next) { for (peer = peers->peer; peer; peer = peer->next) { - if (op->server.len == peer->name.len && ngx_strncmp(op->server.data, peer->name.data, peer->name.len) == 0) { + if (op->server.len == peer->name.len && + ngx_strncmp(op->server.data, peer->name.data, + peer->name.len) == 0) { op->status = NGX_HTTP_BAD_REQUEST; ngx_log_error(NGX_LOG_ERR, log, 0, - "server %V already exists in upstream. %s:%d", - &op->server, - __FUNCTION__, - __LINE__); + "peer %V already exists in upstream", + &op->server); return NGX_ERROR; } - if ( (op->backup == 0 && peers == primary) || (op->backup == 1 && peers == backup) ) { + if ( (op->backup == 0 && peers == primary) || + (op->backup == 1 && peers == backup) ) { last = peer; } } @@ -458,13 +473,11 @@ ngx_dynamic_upstream_stream_op_add(ngx_log_t *log, ngx_dynamic_upstream_op_t *op if (op->backup) { if (backup == NULL) { assert(last == NULL); - backup = ngx_slab_calloc_locked(shpool, sizeof(ngx_stream_upstream_rr_peers_t)); + backup = ngx_slab_calloc_locked(shpool, + sizeof(ngx_stream_upstream_rr_peers_t)); if (backup == NULL) { op->status = NGX_HTTP_INTERNAL_SERVER_ERROR; - ngx_log_error(NGX_LOG_ERR, log, 0, - "failed to allocate memory from slab %s:%d", - __FUNCTION__, - __LINE__); + ngx_log_error(NGX_LOG_ERR, log, 0, "no shared memory"); return NGX_ERROR; } backup->shpool = primary->shpool; @@ -478,20 +491,17 @@ ngx_dynamic_upstream_stream_op_add(ngx_log_t *log, ngx_dynamic_upstream_op_t *op ngx_memzero(&u, sizeof(ngx_url_t)); - u.url.data = ngx_slab_alloc_locked(shpool, op->server.len); + u.url.data = ngx_slab_calloc_locked(shpool, op->server.len + 1); if (u.url.data == NULL) { if (backup && primary->next == NULL) { ngx_slab_free_locked(shpool, backup); } op->status = NGX_HTTP_INTERNAL_SERVER_ERROR; - ngx_log_error(NGX_LOG_ERR, log, 0, - "failed to allocate memory from slab %s:%d", - __FUNCTION__, - __LINE__); + ngx_log_error(NGX_LOG_ERR, log, 0, "no shared memory"); return NGX_ERROR; } - ngx_cpystrn(u.url.data, op->server.data, op->server.len + 1); - u.url.len = op->server.len; + ngx_memcpy(u.url.data, op->server.data, op->server.len); + u.url.len = op->server.len; u.default_port = 80; if (ngx_parse_url_slab(shpool, &u) != NGX_OK) { @@ -506,16 +516,14 @@ ngx_dynamic_upstream_stream_op_add(ngx_log_t *log, ngx_dynamic_upstream_op_t *op return NGX_ERROR; } - new_peer = ngx_slab_calloc_locked(shpool, sizeof(ngx_stream_upstream_rr_peer_t)); + new_peer = ngx_slab_calloc_locked(shpool, + sizeof(ngx_stream_upstream_rr_peer_t)); if (new_peer == NULL) { if (backup && primary->next == NULL) { ngx_slab_free_locked(shpool, backup); } op->status = NGX_HTTP_INTERNAL_SERVER_ERROR; - ngx_log_error(NGX_LOG_ERR, log, 0, - "failed to allocate memory from slab %s:%d", - __FUNCTION__, - __LINE__); + ngx_log_error(NGX_LOG_ERR, log, 0, "no shared memory"); return NGX_ERROR; } @@ -565,63 +573,192 @@ ngx_dynamic_upstream_stream_op_add(ngx_log_t *log, ngx_dynamic_upstream_op_t *op primary->next = backup; } - ngx_log_error(NGX_LOG_NOTICE, log, 0, - "added server %V", &op->server); + ngx_log_error(NGX_LOG_NOTICE, log, 0, "added server %V", &op->server); return NGX_OK; } +typedef ngx_int_t (*cleanup_t) (ngx_slab_pool_t *shpool, void *peer); + + +typedef struct { + ngx_slab_pool_t *shpool; + void *peer; + cleanup_t free; +} ngx_dynamic_cleanup_t; + + +static ngx_connection_t dumb_conn = { + .fd = -1 +}; + + +static void +ngx_dynamic_cleanup(ngx_event_t *ev); + + +static ngx_event_t cleanup_ev = { + .handler = ngx_dynamic_cleanup, + .data = &dumb_conn, + .log = NULL +}; + + +static ngx_array_t *trash = NULL; + + +static ngx_array_t * +ngx_dynamic_trash_init() +{ + trash = ngx_array_create(ngx_cycle->pool, 100, + sizeof(ngx_dynamic_cleanup_t)); + + if (trash) { + cleanup_ev.log = ngx_cycle->log; + ngx_add_timer(&cleanup_ev, 1000); + } + + return trash; +} + + +static void +ngx_dynamic_add_to_trash(ngx_slab_pool_t *shpool, void *peer, cleanup_t cb) +{ + ngx_dynamic_cleanup_t *p; + + if (trash == NULL) { + if (ngx_dynamic_trash_init() == NULL) { + return; + } + } + + p = ngx_array_push(trash); + + if (p != NULL) { + p->shpool = shpool; + p->peer = peer; + p->free = cb; + } +} + + +static void +ngx_dynamic_cleanup(ngx_event_t *ev) +{ + ngx_dynamic_cleanup_t *elts = trash->elts; + ngx_uint_t i, j = 0; + + if (trash->nelts == 0) { + goto settimer; + } + + for (i = 0; i < trash->nelts; i++) { + if (elts[i].free(elts[i].shpool, elts[i].peer) == -1) { + elts[j++] = elts[i]; + } + } + + trash->nelts = j; + +settimer: + + if (!ngx_exiting) { + ngx_add_timer(ev, 1000); + } +} + + static ngx_int_t -ngx_dynamic_upstream_http_op_remove(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, - ngx_slab_pool_t *shpool, ngx_http_upstream_rr_peers_t *primary) +ngx_http_dynamic_upstream_free_peer(ngx_slab_pool_t *shpool, void *p) { - ngx_http_upstream_rr_peer_t *peer, *target, *prev; + ngx_http_upstream_rr_peer_t *peer = p; + + ngx_rwlock_wlock(&peer->lock); + + if (peer->conns == 0) { + if (ngx_dynamic_upstream_is_shpool_range(shpool, peer->name.data)) { + ngx_slab_free_locked(shpool, peer->name.data); + } + + if (ngx_dynamic_upstream_is_shpool_range(shpool, peer->sockaddr)) { + ngx_slab_free_locked(shpool, peer->sockaddr); + } + + ngx_slab_free_locked(shpool, peer); + + return 0; + } + + ngx_rwlock_unlock(&peer->lock); + + return -1; +} + + +static void +ngx_http_dynamic_upstream_op_free_peer(ngx_slab_pool_t *shpool, + ngx_http_upstream_rr_peer_t *peer) +{ + if (ngx_http_dynamic_upstream_free_peer(shpool, peer) == -1) { + /* move to trash */ + ngx_dynamic_add_to_trash(shpool, peer, + ngx_http_dynamic_upstream_free_peer); + } +} + + +static ngx_int_t +ngx_dynamic_upstream_http_op_del(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, + ngx_slab_pool_t *shpool, ngx_http_upstream_rr_peers_t *primary) +{ + ngx_http_upstream_rr_peer_t *peer, *deleted, *prev; ngx_http_upstream_rr_peers_t *peers, *backup; ngx_uint_t weight; + if (shpool == NULL) { + op->status = NGX_HTTP_NOT_IMPLEMENTED; + ngx_log_error(NGX_LOG_ERR, log, 0, + "remove is possible only for upstream with 'zone'"); + return NGX_ERROR; + } + backup = primary->next; - target = NULL; + deleted = NULL; + for (peers = primary; peers; peers = peers->next) { prev = NULL; for (peer = peers->peer; peer; peer = peer->next) { - if (op->server.len == peer->name.len && ngx_strncmp(op->server.data, peer->name.data, peer->name.len) == 0) { + if (op->server.len == peer->name.len && + ngx_strncmp(op->server.data, peer->name.data, + peer->name.len) == 0) { if (peers == primary && peers->number == 1) { op->status = NGX_HTTP_BAD_REQUEST; return NGX_ERROR; } - target = peer; + deleted = peer; peer = peer->next; - goto c; + goto delete; } prev = peer; } } -c: +delete: /* not found */ - if (target == NULL) { + if (deleted == NULL) { op->status = NGX_HTTP_BAD_REQUEST; - ngx_log_error(NGX_LOG_ERR, log, 0, - "server %V is not found. %s:%d", - &op->server, - __FUNCTION__, - __LINE__); + ngx_log_error(NGX_LOG_ERR, log, 0, "server %V is not found", + &op->server); return NGX_ERROR; } - weight = target->weight; - /* released removed peer and attributes */ - if (ngx_dynamic_upstream_is_shpool_range(shpool, target->name.data)) { - ngx_slab_free_locked(shpool, target->name.data); - } - if (ngx_dynamic_upstream_is_shpool_range(shpool, target->sockaddr)) { - ngx_slab_free_locked(shpool, target->sockaddr); - } + weight = deleted->weight; - ngx_slab_free_locked(shpool, target); + ngx_http_dynamic_upstream_op_free_peer(shpool, deleted); /* found head */ if (prev == NULL) { @@ -639,10 +776,11 @@ ngx_dynamic_upstream_http_op_remove(ngx_log_t *log, ngx_dynamic_upstream_op_t *o prev->next = peer; ok: + peers->number--; peers->total_weight -= weight; - peers->single = (peers->number == 1); - peers->weighted = (peers->total_weight != peers->number); + peers->single = peers->number == 1; + peers->weighted = peers->total_weight != peers->number; if (peers->number == 0) { assert(peers == backup); @@ -650,63 +788,100 @@ ngx_dynamic_upstream_http_op_remove(ngx_log_t *log, ngx_dynamic_upstream_op_t *o ngx_slab_free_locked(shpool, backup); } - ngx_log_error(NGX_LOG_NOTICE, log, 0, - "removed server %V", &op->server); + ngx_log_error(NGX_LOG_NOTICE, log, 0, "removed server %V", &op->server); return NGX_OK; } static ngx_int_t -ngx_dynamic_upstream_stream_op_remove(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, - ngx_slab_pool_t *shpool, ngx_stream_upstream_rr_peers_t *primary) +ngx_stream_dynamic_upstream_free_peer(ngx_slab_pool_t *shpool, void *p) { - ngx_stream_upstream_rr_peer_t *peer, *target, *prev; + ngx_stream_upstream_rr_peer_t *peer = p; + + ngx_rwlock_wlock(&peer->lock); + + if (peer->conns == 0) { + if (ngx_dynamic_upstream_is_shpool_range(shpool, peer->name.data)) { + ngx_slab_free_locked(shpool, peer->name.data); + } + + if (ngx_dynamic_upstream_is_shpool_range(shpool, peer->sockaddr)) { + ngx_slab_free_locked(shpool, peer->sockaddr); + } + + ngx_slab_free_locked(shpool, peer); + + return 0; + } + + ngx_rwlock_unlock(&peer->lock); + + return -1; +} + + +static void +ngx_stream_dynamic_upstream_op_free_peer(ngx_slab_pool_t *shpool, + ngx_stream_upstream_rr_peer_t *peer) +{ + if (ngx_stream_dynamic_upstream_free_peer(shpool, peer) == -1) { + /* move to trash */ + ngx_dynamic_add_to_trash(shpool, peer, + ngx_stream_dynamic_upstream_free_peer); + } +} + + +static ngx_int_t +ngx_dynamic_upstream_stream_op_del(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, + ngx_slab_pool_t *shpool, ngx_stream_upstream_rr_peers_t *primary) +{ + ngx_stream_upstream_rr_peer_t *peer, *deleted, *prev; ngx_stream_upstream_rr_peers_t *peers, *backup; ngx_uint_t weight; + if (shpool == NULL) { + op->status = NGX_HTTP_NOT_IMPLEMENTED; + ngx_log_error(NGX_LOG_ERR, log, 0, + "remove is possible only for upstream with 'zone'"); + return NGX_ERROR; + } + backup = primary->next; - target = NULL; + deleted = NULL; + for (peers = primary; peers; peers = peers->next) { prev = NULL; for (peer = peers->peer; peer; peer = peer->next) { - if (op->server.len == peer->name.len && ngx_strncmp(op->server.data, peer->name.data, peer->name.len) == 0) { + if (op->server.len == peer->name.len && + ngx_strncmp(op->server.data, peer->name.data, + peer->name.len) == 0) { if (peers == primary && peers->number == 1) { op->status = NGX_HTTP_BAD_REQUEST; return NGX_ERROR; } - target = peer; + deleted = peer; peer = peer->next; - goto c; + goto delete; } prev = peer; } } -c: +delete: /* not found */ - if (target == NULL) { + if (deleted == NULL) { op->status = NGX_HTTP_BAD_REQUEST; - ngx_log_error(NGX_LOG_ERR, log, 0, - "server %V is not found. %s:%d", - &op->server, - __FUNCTION__, - __LINE__); + ngx_log_error(NGX_LOG_ERR, log, 0, "server %V is not found", + &op->server); return NGX_ERROR; } - weight = target->weight; - /* released removed peer and attributes */ - if (ngx_dynamic_upstream_is_shpool_range(shpool, target->name.data)) { - ngx_slab_free_locked(shpool, target->name.data); - } - - if (ngx_dynamic_upstream_is_shpool_range(shpool, target->sockaddr)) { - ngx_slab_free_locked(shpool, target->sockaddr); - } + weight = deleted->weight; - ngx_slab_free_locked(shpool, target); + ngx_stream_dynamic_upstream_op_free_peer(shpool, deleted); /* found head */ if (prev == NULL) { @@ -724,10 +899,11 @@ ngx_dynamic_upstream_stream_op_remove(ngx_log_t *log, ngx_dynamic_upstream_op_t prev->next = peer; ok: + peers->number--; peers->total_weight -= weight; - peers->single = (peers->number == 1); - peers->weighted = (peers->total_weight != peers->number); + peers->single = peers->number == 1; + peers->weighted = peers->total_weight != peers->number; if (peers->number == 0) { assert(peers == backup); @@ -735,145 +911,147 @@ ngx_dynamic_upstream_stream_op_remove(ngx_log_t *log, ngx_dynamic_upstream_op_t ngx_slab_free_locked(shpool, backup); } - ngx_log_error(NGX_LOG_NOTICE, log, 0, - "removed server %V", &op->server); + ngx_log_error(NGX_LOG_NOTICE, log, 0, "removed server %V", &op->server); return NGX_OK; } static ngx_int_t -ngx_dynamic_upstream_http_op_update_param(ngx_log_t *log, - ngx_dynamic_upstream_op_t *op, - ngx_http_upstream_rr_peers_t *primary) +ngx_dynamic_upstream_http_op_update(ngx_log_t *log, + ngx_dynamic_upstream_op_t *op, ngx_http_upstream_rr_peers_t *primary) { - ngx_http_upstream_rr_peer_t *peer, *target; + ngx_http_upstream_rr_peer_t *peer = NULL; ngx_http_upstream_rr_peers_t *peers; - target = NULL; for (peers = primary; peers; peers = peers->next) { for (peer = peers->peer; peer ; peer = peer->next) { - if (op->server.len == peer->name.len && ngx_strncmp(op->server.data, peer->name.data, peer->name.len) == 0) { - target = peer; - break; + if (op->server.len == peer->name.len && + ngx_strncmp(op->server.data, peer->name.data, + peer->name.len) == 0) { + goto update; } } } - if (target == NULL) { +update: + + if (peer == NULL) { op->status = NGX_HTTP_BAD_REQUEST; - ngx_log_error(NGX_LOG_ERR, log, 0, - "peer %V is not found. %s:%d", - &op->server, - __FUNCTION__, - __LINE__); + ngx_log_error(NGX_LOG_ERR, log, 0, "peer %V is not found", &op->server); return NGX_ERROR; } + ngx_http_upstream_rr_peer_lock(primary, peer); + if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_WEIGHT) { - target->weight = op->weight; - target->current_weight = op->weight; - target->effective_weight = op->weight; + peers->total_weight -= peer->weight; + peers->total_weight += op->weight; + peers->weighted = peers->total_weight != peers->number; + peer->weight = op->weight; + peer->current_weight = op->weight; + peer->effective_weight = op->weight; } if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_MAX_FAILS) { - target->max_fails = op->max_fails; + peer->max_fails = op->max_fails; } #if defined(nginx_version) && (nginx_version >= 1011005) if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_MAX_CONNS) { - target->max_conns = op->max_conns; + peer->max_conns = op->max_conns; } #endif if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_FAIL_TIMEOUT) { - target->fail_timeout = op->fail_timeout; + peer->fail_timeout = op->fail_timeout; } if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_UP) { - target->down = 0; - target->checked = ngx_time(); - target->fails = 0; - ngx_log_error(NGX_LOG_NOTICE, log, 0, - "up peer %V", &op->server); + peer->down = 0; + peer->checked = ngx_time(); + peer->fails = 0; + ngx_log_error(NGX_LOG_NOTICE, log, 0, "up peer %V", &op->server); } if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_DOWN) { - target->down = 1; - target->checked = ngx_time(); - target->fails = target->max_fails; - ngx_log_error(NGX_LOG_NOTICE, log, 0, - "down peer %V", &op->server); + peer->down = 1; + peer->checked = ngx_time(); + peer->fails = peer->max_fails; + ngx_log_error(NGX_LOG_NOTICE, log, 0, "down peer %V", &op->server); } + ngx_http_upstream_rr_peer_unlock(primary, peer); + return NGX_OK; } static ngx_int_t -ngx_dynamic_upstream_stream_op_update_param(ngx_log_t *log, - ngx_dynamic_upstream_op_t *op, - ngx_stream_upstream_rr_peers_t *primary) +ngx_dynamic_upstream_stream_op_update(ngx_log_t *log, + ngx_dynamic_upstream_op_t *op, ngx_stream_upstream_rr_peers_t *primary) { - ngx_stream_upstream_rr_peer_t *peer, *target; + ngx_stream_upstream_rr_peer_t *peer = NULL; ngx_stream_upstream_rr_peers_t *peers; - target = NULL; - for (peers = primary; peers; peers = peers->next) { for (peer = peers->peer; peer ; peer = peer->next) { - if (op->server.len == peer->name.len && ngx_strncmp(op->server.data, peer->name.data, peer->name.len) == 0) { - target = peer; - break; + if (op->server.len == peer->name.len && + ngx_strncmp(op->server.data, peer->name.data, + peer->name.len) == 0) { + goto update; } } } - if (target == NULL) { +update: + + if (peer == NULL) { op->status = NGX_HTTP_BAD_REQUEST; - ngx_log_error(NGX_LOG_ERR, log, 0, - "peer %V is not found. %s:%d", - &op->server, - __FUNCTION__, - __LINE__); + ngx_log_error(NGX_LOG_ERR, log, 0, "peer %V is not found", &op->server); return NGX_ERROR; } + ngx_stream_upstream_rr_peer_lock(primary, peer); + if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_WEIGHT) { - target->weight = op->weight; - target->current_weight = op->weight; - target->effective_weight = op->weight; + peers->total_weight -= peer->weight; + peers->total_weight += op->weight; + peers->weighted = peers->total_weight != peers->number; + peer->weight = op->weight; + peer->current_weight = op->weight; + peer->effective_weight = op->weight; } if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_MAX_FAILS) { - target->max_fails = op->max_fails; + peer->max_fails = op->max_fails; } #if defined(nginx_version) && (nginx_version >= 1011005) if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_MAX_CONNS) { - target->max_conns = op->max_conns; + peer->max_conns = op->max_conns; } #endif if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_FAIL_TIMEOUT) { - target->fail_timeout = op->fail_timeout; + peer->fail_timeout = op->fail_timeout; } if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_UP) { - target->down = 0; - target->checked = ngx_time(); - target->fails = 0; - ngx_log_error(NGX_LOG_NOTICE, log, 0, - "up peer %V", &op->server); + peer->down = 0; + peer->checked = ngx_time(); + peer->fails = 0; + ngx_log_error(NGX_LOG_NOTICE, log, 0, "up peer %V", &op->server); } if (op->op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_DOWN) { - target->down = 1; - target->checked = ngx_time(); - target->fails = target->max_fails; - ngx_log_error(NGX_LOG_NOTICE, log, 0, - "down peer %V", &op->server); + peer->down = 1; + peer->checked = ngx_time(); + peer->fails = peer->max_fails; + ngx_log_error(NGX_LOG_NOTICE, log, 0, "down peer %V", &op->server); } + ngx_stream_upstream_rr_peer_unlock(primary, peer); + return NGX_OK; } diff --git a/src/ngx_http_dynamic_upstream_module.c b/src/ngx_http_dynamic_upstream_module.c new file mode 100644 index 0000000..2d0df16 --- /dev/null +++ b/src/ngx_http_dynamic_upstream_module.c @@ -0,0 +1,406 @@ +#include +#include +#include + +#include "ngx_dynamic_upstream_module.h" +#include "ngx_dynamic_upstream_op.h" + + +static ngx_http_upstream_srv_conf_t * +ngx_http_dynamic_upstream_get_zone(ngx_http_request_t *r, + ngx_dynamic_upstream_op_t *op); + + +static ngx_stream_upstream_srv_conf_t * +ngx_stream_dynamic_upstream_get_zone(ngx_dynamic_upstream_op_t *op); + + +static ngx_int_t +ngx_http_dynamic_upstream_response(ngx_http_upstream_rr_peers_t *peers, + ngx_buf_t *b, size_t size, ngx_int_t verbose); + + +static ngx_int_t +ngx_stream_dynamic_upstream_response(ngx_stream_upstream_rr_peers_t *peers, + ngx_buf_t *b, size_t size, ngx_int_t verbose); + + +static ngx_int_t +ngx_dynamic_upstream_handler(ngx_http_request_t *r); + + +static char * +ngx_dynamic_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +static ngx_command_t ngx_http_dynamic_upstream_commands[] = { + { + ngx_string("dynamic_upstream"), + NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS, + ngx_dynamic_upstream, + 0, + 0, + NULL + }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_dynamic_upstream_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main */ + NULL, /* init main */ + + NULL, /* create server */ + NULL, /* merge server */ + + NULL, /* create location */ + NULL /* merge location */ +}; + + +ngx_module_t ngx_http_dynamic_upstream_module = { + NGX_MODULE_V1, + &ngx_http_dynamic_upstream_module_ctx, /* module context */ + ngx_http_dynamic_upstream_commands, /* module directives */ + NGX_HTTP_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 ngx_http_upstream_srv_conf_t * +ngx_http_dynamic_upstream_get_zone(ngx_http_request_t *r, + ngx_dynamic_upstream_op_t *op) +{ + ngx_uint_t i; + ngx_http_upstream_srv_conf_t *uscf, **uscfp; + ngx_http_upstream_main_conf_t *umcf; + + umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); + if (umcf == NULL) { + return NULL; + } + uscfp = umcf->upstreams.elts; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + uscf = uscfp[i]; + if (uscf->shm_zone != NULL && + uscf->shm_zone->shm.name.len == op->upstream.len && + ngx_strncmp(uscf->shm_zone->shm.name.data, op->upstream.data, + op->upstream.len) == 0) + { + return uscf; + } + } + + return NULL; +} + + +static ngx_stream_upstream_srv_conf_t * +ngx_stream_dynamic_upstream_get_zone(ngx_dynamic_upstream_op_t *op) +{ + ngx_uint_t i; + ngx_stream_upstream_srv_conf_t *uscf, **uscfp; + ngx_stream_upstream_main_conf_t *umcf; + + umcf = ngx_stream_cycle_get_module_main_conf(ngx_cycle, + ngx_stream_upstream_module); + if (umcf == NULL) { + return NULL; + } + uscfp = umcf->upstreams.elts; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + uscf = uscfp[i]; + if (uscf->shm_zone != NULL && + uscf->shm_zone->shm.name.len == op->upstream.len && + ngx_strncmp(uscf->shm_zone->shm.name.data, op->upstream.data, + op->upstream.len) == 0) + { + return uscf; + } + } + + return NULL; +} + + +static ngx_int_t +ngx_http_dynamic_upstream_response(ngx_http_upstream_rr_peers_t *peers, + ngx_buf_t *b, size_t size, ngx_int_t verbose) +{ + ngx_http_upstream_rr_peer_t *peer; + ngx_http_upstream_rr_peers_t *primary, *backup; + u_char *last; + + primary = peers; + + ngx_http_upstream_rr_peers_rlock(primary); + + backup = primary->next; + + last = b->last + size; + + for (; peers; peers = peers->next) { + for (peer = peers->peer; peer; peer = peer->next) { + + if (verbose) { + b->last = ngx_snprintf(b->last, last - b->last, + "server %V weight=%d max_fails=%d fail_timeout=%d" +#if defined(nginx_version) && (nginx_version >= 1011005) + " max_conns=%d" +#endif + " conns=%d", + &peer->name, peer->weight, peer->max_fails, + peer->fail_timeout, +#if defined(nginx_version) && (nginx_version >= 1011005) + peer->max_conns, +#endif + peer->conns); + + } else { + b->last = ngx_snprintf(b->last, last - b->last, + "server %V", &peer->name); + } + + b->last = peer->down + ? ngx_snprintf(b->last, last - b->last, " down") + : ngx_snprintf(b->last, last - b->last, ""); + b->last = peers == backup + ? ngx_snprintf(b->last, last - b->last, " backup;\n") + : ngx_snprintf(b->last, last - b->last, ";\n"); + } + } + + ngx_http_upstream_rr_peers_unlock(primary); + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_dynamic_upstream_response(ngx_stream_upstream_rr_peers_t *peers, + ngx_buf_t *b, size_t size, ngx_int_t verbose) +{ + ngx_stream_upstream_rr_peer_t *peer; + ngx_stream_upstream_rr_peers_t *primary, *backup; + u_char *last; + + primary = peers; + + ngx_http_upstream_rr_peers_rlock(primary); + + backup = primary->next; + + last = b->last + size; + + for (; peers; peers = peers->next) { + for (peer = peers->peer; peer; peer = peer->next) { + + if (verbose) { + b->last = ngx_snprintf(b->last, last - b->last, + "server %V weight=%d max_fails=%d fail_timeout=%d" +#if defined(nginx_version) && (nginx_version >= 1011005) + " max_conns=%d" +#endif + " conns=%d", + &peer->name, peer->weight, peer->max_fails, + peer->fail_timeout, +#if defined(nginx_version) && (nginx_version >= 1011005) + peer->max_conns, +#endif + peer->conns); + } else { + b->last = ngx_snprintf(b->last, last - b->last, + "server %V", &peer->name); + } + + b->last = peer->down + ? ngx_snprintf(b->last, last - b->last, " down") + : ngx_snprintf(b->last, last - b->last, ""); + b->last = peers == backup + ? ngx_snprintf(b->last, last - b->last, " backup;\n") + : ngx_snprintf(b->last, last - b->last, ";\n"); + } + } + + ngx_http_upstream_rr_peers_unlock(primary); + + return NGX_OK; +} + + +typedef struct { + ngx_http_upstream_srv_conf_t *http; + ngx_stream_upstream_srv_conf_t *stream; +} ngx_upstream_srv_conf_t; + + +static ngx_int_t +ngx_dynamic_upstream_handler(ngx_http_request_t *r) +{ + size_t size; + ngx_int_t rc; + ngx_chain_t out; + ngx_dynamic_upstream_op_t op; + ngx_buf_t *b; + ngx_upstream_srv_conf_t uscf; + + ngx_memzero(&uscf, sizeof(ngx_upstream_srv_conf_t)); + + if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) { + return NGX_HTTP_NOT_ALLOWED; + } + + rc = ngx_http_discard_request_body(r); + + if (rc != NGX_OK) { + return rc; + } + + r->headers_out.content_type_len = sizeof("text/plain") - 1; + ngx_str_set(&r->headers_out.content_type, "text/plain"); + r->headers_out.content_type_lowcase = NULL; + + if (r->method == NGX_HTTP_HEAD) { + r->headers_out.status = NGX_HTTP_OK; + + rc = ngx_http_send_header(r); + + if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { + return rc; + } + } + + rc = ngx_dynamic_upstream_build_op(r, &op); + if (rc != NGX_OK) { + if (op.status == NGX_HTTP_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + return op.status; + } + + if (op.op_param & NGX_DYNAMIC_UPSTEAM_OP_PARAM_STREAM) { + uscf.stream = ngx_stream_dynamic_upstream_get_zone(&op); + } else { + uscf.http = ngx_http_dynamic_upstream_get_zone(r, &op); + } + + if (uscf.stream == NULL && uscf.http == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream is not found. %s:%d", + __FUNCTION__, + __LINE__); + return NGX_HTTP_NOT_FOUND; + } + + if (uscf.stream) { + rc = ngx_dynamic_upstream_stream_op(r->connection->log, &op, + uscf.stream); + size = uscf.stream->shm_zone->shm.size; + } else { + rc = ngx_dynamic_upstream_op(r->connection->log, &op, uscf.http); + size = uscf.http->shm_zone->shm.size; + } + + if (rc != NGX_OK) { + if (op.status == NGX_HTTP_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + return op.status; + } + + + b = ngx_create_temp_buf(r->pool, size); + if (b == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + out.buf = b; + out.next = NULL; + + rc = uscf.stream + ? ngx_stream_dynamic_upstream_response(uscf.stream->peer.data, b, + size, op.verbose) + : ngx_http_dynamic_upstream_response(uscf.http->peer.data, b, + size, op.verbose); + + if (rc == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "failed to create a response. %s:%d", + __FUNCTION__, + __LINE__); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->headers_out.status = NGX_HTTP_OK; + r->headers_out.content_length_n = b->last - b->pos; + + b->last_buf = (r == r->main) ? 1 : 0; + b->last_in_chain = 1; + + rc = ngx_http_send_header(r); + + if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { + return rc; + } + + return ngx_http_output_filter(r, &out); +} + + +static char * +ngx_dynamic_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_loc_conf_t *clcf; + + clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); + clcf->handler = ngx_dynamic_upstream_handler; + + return NGX_CONF_OK; +} + + +ngx_int_t +ngx_dynamic_upstream_op(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, + ngx_http_upstream_srv_conf_t *uscf) +{ + ngx_upstream_rr_peers_t peers; + ngx_slab_pool_t *slab_pool = NULL; + + if (uscf->shm_zone) { + slab_pool = (ngx_slab_pool_t *) uscf->shm_zone->shm.addr; + } + + peers.http = uscf->peer.data; + + return ngx_dynamic_upstream_op_impl(log, op, slab_pool, &peers); +} + + +ngx_int_t +ngx_dynamic_upstream_stream_op(ngx_log_t *log, ngx_dynamic_upstream_op_t *op, + ngx_stream_upstream_srv_conf_t *uscf) +{ + ngx_upstream_rr_peers_t peers; + ngx_slab_pool_t *slab_pool = NULL; + + if (uscf->shm_zone) { + slab_pool = (ngx_slab_pool_t *) uscf->shm_zone->shm.addr; + } + + peers.stream = uscf->peer.data; + + return ngx_dynamic_upstream_op_impl(log, op, slab_pool, &peers); +} diff --git a/t/00-list.t b/t/00-list.t index c53e093..e39816a 100644 --- a/t/00-list.t +++ b/t/00-list.t @@ -44,9 +44,9 @@ server 127.0.0.1:6003; --- request GET /dynamic?upstream=zone_for_backends&verbose= --- response_body -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; === TEST 3: not found upstream diff --git a/t/00-stream-list.t b/t/00-stream-list.t index 036bc97..68a9e3e 100644 --- a/t/00-stream-list.t +++ b/t/00-stream-list.t @@ -49,9 +49,9 @@ server 127.0.0.1:6003; --- request GET /dynamic?upstream=zone_for_backends&verbose=&stream= --- response_body -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; === TEST 3: not found upstream diff --git a/t/01-stream-update-param.t b/t/01-stream-update-param.t index a7163e5..d228c7d 100644 --- a/t/01-stream-update-param.t +++ b/t/01-stream-update-param.t @@ -27,9 +27,9 @@ __DATA__ --- request GET /dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&weight=10&max_fails=5&fail_timeout=5&stream= --- response_body -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=10 max_fails=5 fail_timeout=5; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=10 max_fails=5 fail_timeout=5 max_conns=0 conns=0; === TEST 2: update weight parameter @@ -49,9 +49,9 @@ server 127.0.0.1:6003 weight=10 max_fails=5 fail_timeout=5; --- request GET /dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&weight=10&stream= --- response_body -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=10 max_fails=1 fail_timeout=10; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=10 max_fails=1 fail_timeout=10 max_conns=0 conns=0; === TEST 3: update max_fails parameter @@ -71,9 +71,9 @@ server 127.0.0.1:6003 weight=10 max_fails=1 fail_timeout=10; --- request GET /dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&max_fails=5&stream= --- response_body -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=1 max_fails=5 fail_timeout=10; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=1 max_fails=5 fail_timeout=10 max_conns=0 conns=0; === TEST 4: update fail_timeout parameter @@ -93,9 +93,9 @@ server 127.0.0.1:6003 weight=1 max_fails=5 fail_timeout=10; --- request GET /dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&fail_timeout=5&stream= --- response_body -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=5; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=5 max_conns=0 conns=0; === TEST 5: fail to update weight parameter @@ -156,3 +156,25 @@ server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=5; GET /dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&fail_timeout=abc&stream= --- response_body_like: 400 Bad Request --- error_code: 400 + + +=== TEST 8: update max_conns parameter +--- stream_config + upstream backends { + zone zone_for_backends 128k; + server 127.0.0.1:6001; + server 127.0.0.1:6002; + server 127.0.0.1:6003; + } +--- stream_server_config + proxy_pass backends; +--- config + location /dynamic { + dynamic_upstream; + } +--- request + GET /dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&max_conns=5&stream= +--- response_body +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10 max_conns=5 conns=0; diff --git a/t/01-update-param.t b/t/01-update-param.t index 2305cb9..9c3099d 100644 --- a/t/01-update-param.t +++ b/t/01-update-param.t @@ -24,9 +24,9 @@ __DATA__ --- request GET /dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&weight=10&max_fails=5&fail_timeout=5 --- response_body -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=10 max_fails=5 fail_timeout=5; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=10 max_fails=5 fail_timeout=5 max_conns=0 conns=0; === TEST 2: update weight parameter @@ -44,9 +44,9 @@ server 127.0.0.1:6003 weight=10 max_fails=5 fail_timeout=5; --- request GET /dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&weight=10 --- response_body -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=10 max_fails=1 fail_timeout=10; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=10 max_fails=1 fail_timeout=10 max_conns=0 conns=0; === TEST 3: update max_fails parameter @@ -64,9 +64,9 @@ server 127.0.0.1:6003 weight=10 max_fails=1 fail_timeout=10; --- request GET /dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&max_fails=5 --- response_body -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=1 max_fails=5 fail_timeout=10; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=1 max_fails=5 fail_timeout=10 max_conns=0 conns=0; === TEST 4: update fail_timeout parameter @@ -84,9 +84,9 @@ server 127.0.0.1:6003 weight=1 max_fails=5 fail_timeout=10; --- request GET /dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&fail_timeout=5 --- response_body -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=5; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=5 max_conns=0 conns=0; === TEST 5: fail to update weight parameter @@ -141,3 +141,22 @@ server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=5; GET /dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&fail_timeout=abc --- response_body_like: 400 Bad Request --- error_code: 400 + +=== TEST 8: update max_conns parameter +--- http_config + upstream backends { + zone zone_for_backends 128k; + server 127.0.0.1:6001; + server 127.0.0.1:6002; + server 127.0.0.1:6003; + } +--- config + location /dynamic { + dynamic_upstream; + } +--- request + GET /dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&max_conns=5 +--- response_body +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10 max_conns=5 conns=0; diff --git a/t/02-down.t b/t/02-down.t index 82327ec..8b42ee3 100644 --- a/t/02-down.t +++ b/t/02-down.t @@ -24,9 +24,9 @@ __DATA__ --- request GET /dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&down= --- response_body -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10 down; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0 down; === TEST 2: down and up diff --git a/t/02-stream-down.t b/t/02-stream-down.t index 52a8519..5ac1e39 100644 --- a/t/02-stream-down.t +++ b/t/02-stream-down.t @@ -27,9 +27,9 @@ __DATA__ --- request GET /dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&down=&stream= --- response_body -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10 down; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0 down; === TEST 2: down and up diff --git a/t/03-add.t b/t/03-add.t index 58f8b70..60f35d9 100644 --- a/t/03-add.t +++ b/t/03-add.t @@ -45,10 +45,10 @@ server 127.0.0.1:6004; --- request GET /dynamic?upstream=zone_for_backends&server=127.0.0.1:6004&add=&weight=10 --- response_body -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6004 weight=10 max_fails=1 fail_timeout=10; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6004 weight=10 max_fails=1 fail_timeout=10 max_conns=0 conns=0; === TEST 3: add duplicated server diff --git a/t/03-stream-add.t b/t/03-stream-add.t index 6bb18b6..4e703e3 100644 --- a/t/03-stream-add.t +++ b/t/03-stream-add.t @@ -50,10 +50,10 @@ server 127.0.0.1:6004; --- request GET /dynamic?upstream=zone_for_backends&server=127.0.0.1:6004&add=&weight=10&stream= --- response_body -server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10; -server 127.0.0.1:6004 weight=10 max_fails=1 fail_timeout=10; +server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10 max_conns=0 conns=0; +server 127.0.0.1:6004 weight=10 max_fails=1 fail_timeout=10 max_conns=0 conns=0; === TEST 3: add duplicated server