Merge pull request 'nginx主动探测示例代码' (#14) from myfeji8p7/Nginx:jfu into master
commit
ecc5d8a95d
@ -1 +0,0 @@
|
||||
Subproject commit 87bfa66ddf16c17053ba7bbae72400c9939ecf6d
|
@ -0,0 +1,347 @@
|
||||
Name
|
||||
nginx_http_upstream_check_module - support upstream health check with
|
||||
Nginx
|
||||
|
||||
Synopsis
|
||||
http {
|
||||
|
||||
upstream cluster {
|
||||
|
||||
# simple round-robin
|
||||
server 192.168.0.1:80;
|
||||
server 192.168.0.2:80;
|
||||
|
||||
check interval=5000 rise=1 fall=3 timeout=4000;
|
||||
|
||||
#check interval=3000 rise=2 fall=5 timeout=1000 type=ssl_hello;
|
||||
|
||||
#check interval=3000 rise=2 fall=5 timeout=1000 type=http;
|
||||
#check_http_send "HEAD / HTTP/1.0\r\n\r\n";
|
||||
#check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
location / {
|
||||
proxy_pass http://cluster;
|
||||
}
|
||||
|
||||
location /status {
|
||||
check_status;
|
||||
|
||||
access_log off;
|
||||
allow SOME.IP.ADD.RESS;
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Description
|
||||
Add the support of health check with the upstream servers.
|
||||
|
||||
Directives
|
||||
check
|
||||
syntax: *check interval=milliseconds [fall=count] [rise=count]
|
||||
[timeout=milliseconds] [default_down=true|false]
|
||||
[type=tcp|http|ssl_hello|mysql|ajp|fastcgi]*
|
||||
|
||||
default: *none, if parameters omitted, default parameters are
|
||||
interval=30000 fall=5 rise=2 timeout=1000 default_down=true type=tcp*
|
||||
|
||||
context: *upstream*
|
||||
|
||||
description: Add the health check for the upstream servers.
|
||||
|
||||
The parameters' meanings are:
|
||||
|
||||
* *interval*: the check request's interval time.
|
||||
|
||||
* *fall*(fall_count): After fall_count check failures, the server is
|
||||
marked down.
|
||||
|
||||
* *rise*(rise_count): After rise_count check success, the server is
|
||||
marked up.
|
||||
|
||||
* *timeout*: the check request's timeout.
|
||||
|
||||
* *default_down*: set initial state of backend server, default is
|
||||
down.
|
||||
|
||||
* *port*: specify the check port in the backend servers. It can be
|
||||
different with the original servers port. Default the port is 0 and
|
||||
it means the same as the original backend server.
|
||||
|
||||
* *type*: the check protocol type:
|
||||
|
||||
1. *tcp* is a simple tcp socket connect and peek one byte.
|
||||
|
||||
2. *ssl_hello* sends a client ssl hello packet and receives the
|
||||
server ssl hello packet.
|
||||
|
||||
3. *http* sends a http request packet, receives and parses the http
|
||||
response to diagnose if the upstream server is alive.
|
||||
|
||||
4. *mysql* connects to the mysql server, receives the greeting
|
||||
response to diagnose if the upstream server is alive.
|
||||
|
||||
5. *ajp* sends a AJP Cping packet, receives and parses the AJP
|
||||
Cpong response to diagnose if the upstream server is alive.
|
||||
|
||||
6. *fastcgi* send a fastcgi request, receives and parses the
|
||||
fastcgi response to diagnose if the upstream server is alive.
|
||||
|
||||
check_http_send
|
||||
syntax: *check_http_send http_packet*
|
||||
|
||||
default: *"GET / HTTP/1.0\r\n\r\n"*
|
||||
|
||||
context: *upstream*
|
||||
|
||||
description: If you set the check type is http, then the check function
|
||||
will sends this http packet to check the upstream server.
|
||||
|
||||
check_http_expect_alive
|
||||
syntax: *check_http_expect_alive [ http_2xx | http_3xx | http_4xx |
|
||||
http_5xx ]*
|
||||
|
||||
default: *http_2xx | http_3xx*
|
||||
|
||||
context: *upstream*
|
||||
|
||||
description: These status codes indicate the upstream server's http
|
||||
response is ok, the backend is alive.
|
||||
|
||||
check_keepalive_requests
|
||||
syntax: *check_keepalive_requests num*
|
||||
|
||||
default: *check_keepalive_requests 1*
|
||||
|
||||
context: *upstream*
|
||||
|
||||
description: The directive specifies the number of requests sent on a
|
||||
connection, the default vaule 1 indicates that nginx will certainly
|
||||
close the connection after a request.
|
||||
|
||||
check_fastcgi_param
|
||||
Syntax: *check_fastcgi_params parameter value*
|
||||
|
||||
default: see below
|
||||
|
||||
context: *upstream*
|
||||
|
||||
description: If you set the check type is fastcgi, then the check
|
||||
function will sends this fastcgi headers to check the upstream server.
|
||||
The default directive looks like:
|
||||
|
||||
check_fastcgi_param "REQUEST_METHOD" "GET";
|
||||
check_fastcgi_param "REQUEST_URI" "/";
|
||||
check_fastcgi_param "SCRIPT_FILENAME" "index.php";
|
||||
|
||||
check_shm_size
|
||||
syntax: *check_shm_size size*
|
||||
|
||||
default: *1M*
|
||||
|
||||
context: *http*
|
||||
|
||||
description: Default size is one megabytes. If you check thousands of
|
||||
servers, the shared memory for health check may be not enough, you can
|
||||
enlarge it with this directive.
|
||||
|
||||
check_status
|
||||
syntax: *check_status [html|csv|json]*
|
||||
|
||||
default: *none*
|
||||
|
||||
context: *location*
|
||||
|
||||
description: Display the health checking servers' status by HTTP. This
|
||||
directive should be set in the http block.
|
||||
|
||||
You can specify the default display format. The formats can be `html`,
|
||||
`csv` or `json`. The default type is `html`. It also supports to specify
|
||||
the format by the request argument. Suppose your `check_status` location
|
||||
is '/status', the argument of `format` can change the display page's
|
||||
format. You can do like this:
|
||||
|
||||
/status?format=html
|
||||
/status?format=csv
|
||||
/status?format=json
|
||||
|
||||
At present, you can fetch the list of servers with the same status by
|
||||
the argument of `status`. For example:
|
||||
|
||||
/status?format=html&status=down
|
||||
/status?format=csv&status=up
|
||||
|
||||
Below it's the sample html page:
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<title>Nginx http upstream check status</title>
|
||||
<h1>Nginx http upstream check status</h1>
|
||||
<h2>Check upstream server number: 1, generation: 3</h2>
|
||||
<th>Index</th>
|
||||
<th>Upstream</th>
|
||||
<th>Name</th>
|
||||
<th>Status</th>
|
||||
<th>Rise counts</th>
|
||||
<th>Fall counts</th>
|
||||
<th>Check type</th>
|
||||
<th>Check port</th>
|
||||
<td>0</td>
|
||||
<td>backend</td>
|
||||
<td>106.187.48.116:80</td>
|
||||
<td>up</td>
|
||||
<td>39</td>
|
||||
<td>0</td>
|
||||
<td>http</td>
|
||||
<td>80</td>
|
||||
|
||||
Below it's the sample of csv page:
|
||||
|
||||
0,backend,106.187.48.116:80,up,46,0,http,80
|
||||
|
||||
Below it's the sample of json page:
|
||||
|
||||
{"servers": {
|
||||
"total": 1,
|
||||
"generation": 3,
|
||||
"server": [
|
||||
{"index": 0, "upstream": "backend", "name": "106.187.48.116:80", "status": "up", "rise": 58, "fall": 0, "type": "http", "port": 80}
|
||||
]
|
||||
}}
|
||||
|
||||
Installation
|
||||
Download the latest version of the release tarball of this module from
|
||||
github (<http://github.com/yaoweibin/nginx_upstream_check_module>)
|
||||
|
||||
Grab the nginx source code from nginx.org (<http://nginx.org/>), for
|
||||
example, the version 1.0.14 (see nginx compatibility), and then build
|
||||
the source with this module:
|
||||
|
||||
$ wget 'http://nginx.org/download/nginx-1.0.14.tar.gz'
|
||||
$ tar -xzvf nginx-1.0.14.tar.gz
|
||||
$ cd nginx-1.0.14/
|
||||
$ patch -p1 < /path/to/nginx_http_upstream_check_module/check.patch
|
||||
|
||||
$ ./configure --add-module=/path/to/nginx_http_upstream_check_module
|
||||
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
Note
|
||||
If you use nginx-1.2.1 or nginx-1.3.0, the nginx upstream round robin
|
||||
module changed greatly. You should use the patch named
|
||||
'check_1.2.1.patch'.
|
||||
|
||||
If you use nginx-1.2.2+ or nginx-1.3.1+, It added the upstream
|
||||
least_conn module. You should use the patch named 'check_1.2.2+.patch'.
|
||||
|
||||
If you use nginx-1.2.6+ or nginx-1.3.9+, It adjusted the round robin
|
||||
module. You should use the patch named 'check_1.2.6+.patch'.
|
||||
|
||||
If you use nginx-1.5.12+, You should use the patch named
|
||||
'check_1.5.12+.patch'.
|
||||
|
||||
If you use nginx-1.7.2+, You should use the patch named
|
||||
'check_1.7.2+.patch'.
|
||||
|
||||
The patch just adds the support for the official Round-Robin, Ip_hash
|
||||
and least_conn upstream module. But it's easy to expand my module to
|
||||
other upstream modules. See the patch for detail.
|
||||
|
||||
If you want to add the support for upstream fair module, you can do it
|
||||
like this:
|
||||
|
||||
$ git clone git://github.com/gnosek/nginx-upstream-fair.git
|
||||
$ cd nginx-upstream-fair
|
||||
$ patch -p2 < /path/to/nginx_http_upstream_check_module/upstream_fair.patch
|
||||
$ cd /path/to/nginx-1.0.14
|
||||
$ ./configure --add-module=/path/to/nginx_http_upstream_check_module --add-module=/path/to/nginx-upstream-fair-module
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
If you want to add the support for nginx sticky module, you can do it
|
||||
like this:
|
||||
|
||||
$ svn checkout http://nginx-sticky-module.googlecode.com/svn/trunk/ nginx-sticky-module
|
||||
$ cd nginx-sticky-module
|
||||
$ patch -p0 < /path/to/nginx_http_upstream_check_module/nginx-sticky-module.patch
|
||||
$ cd /path/to/nginx-1.0.14
|
||||
$ ./configure --add-module=/path/to/nginx_http_upstream_check_module --add-module=/path/to/nginx-sticky-module
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
Note that, the nginx-sticky-module also needs the original check.patch.
|
||||
|
||||
Compatibility
|
||||
* The module version 0.1.5 should be compatibility with 0.7.67+
|
||||
|
||||
* The module version 0.1.8 should be compatibility with Nginx-1.0.14+
|
||||
|
||||
Notes
|
||||
TODO
|
||||
Known Issues
|
||||
Changelogs
|
||||
v0.3
|
||||
* support keepalive check requests
|
||||
|
||||
* fastcgi check requests
|
||||
|
||||
* json/csv check status page support
|
||||
|
||||
v0.1
|
||||
* first release
|
||||
|
||||
Authors
|
||||
Weibin Yao(姚伟斌) *yaoweibin at gmail dot com*
|
||||
|
||||
Matthieu Tourne
|
||||
|
||||
Copyright & License
|
||||
This README template copy from agentzh (<http://github.com/agentzh>).
|
||||
|
||||
The health check part is borrowed the design of Jack Lindamood's
|
||||
healthcheck module healthcheck_nginx_upstreams
|
||||
(<http://github.com/cep21/healthcheck_nginx_upstreams>);
|
||||
|
||||
This module is licensed under the BSD license.
|
||||
|
||||
Copyright (C) 2014 by Weibin Yao <yaoweibin@gmail.com>
|
||||
|
||||
Copyright (C) 2010-2014 Alibaba Group Holding Limited
|
||||
|
||||
Copyright (C) 2014 by LiangBin Li
|
||||
|
||||
Copyright (C) 2014 by Zhuo Yuan
|
||||
|
||||
Copyright (C) 2012 by Matthieu Tourne
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
@ -0,0 +1,192 @@
|
||||
diff --git a/src/http/modules/ngx_http_upstream_ip_hash_module.c b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
index fd9ecbe..d3849b6 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
@@ -9,6 +9,10 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
+
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
@@ -182,6 +186,12 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
|
||||
if (!peer->down) {
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get ip_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (!ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+#endif
|
||||
if (peer->max_fails == 0 || peer->fails < peer->max_fails) {
|
||||
break;
|
||||
}
|
||||
@@ -190,6 +200,9 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
peer->fails = 0;
|
||||
break;
|
||||
}
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ }
|
||||
+#endif
|
||||
}
|
||||
|
||||
iphp->rrp.tried[n] |= m;
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c
|
||||
index afc9b2e..1c0344e 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.c
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
static ngx_int_t ngx_http_upstream_cmp_servers(const void *one,
|
||||
const void *two);
|
||||
@@ -75,6 +78,17 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peers->peer[n].down = server[i].down;
|
||||
peers->peer[n].weight = server[i].down ? 0 : server[i].weight;
|
||||
peers->peer[n].current_weight = peers->peer[n].weight;
|
||||
+
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peers->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ peers->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
n++;
|
||||
}
|
||||
}
|
||||
@@ -128,6 +142,17 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
backup->peer[n].max_fails = server[i].max_fails;
|
||||
backup->peer[n].fail_timeout = server[i].fail_timeout;
|
||||
backup->peer[n].down = server[i].down;
|
||||
+
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ backup->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ backup->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
n++;
|
||||
}
|
||||
}
|
||||
@@ -186,6 +211,9 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peers->peer[i].current_weight = 1;
|
||||
peers->peer[i].max_fails = 1;
|
||||
peers->peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
}
|
||||
|
||||
us->peer.data = peers;
|
||||
@@ -302,6 +330,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peers->peer[0].current_weight = 1;
|
||||
peers->peer[0].max_fails = 1;
|
||||
peers->peer[0].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[0].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
|
||||
} else {
|
||||
|
||||
@@ -334,6 +365,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peers->peer[i].current_weight = 1;
|
||||
peers->peer[i].max_fails = 1;
|
||||
peers->peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,7 +445,11 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
|
||||
if (rrp->peers->single) {
|
||||
peer = &rrp->peers->peer[0];
|
||||
-
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ return NGX_BUSY;
|
||||
+ }
|
||||
+#endif
|
||||
} else {
|
||||
|
||||
/* there are several peers */
|
||||
@@ -438,6 +476,12 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
|
||||
if (!peer->down) {
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get rr peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (!ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+#endif
|
||||
if (peer->max_fails == 0
|
||||
|| peer->fails < peer->max_fails)
|
||||
{
|
||||
@@ -448,6 +492,9 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
peer->fails = 0;
|
||||
break;
|
||||
}
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ }
|
||||
+#endif
|
||||
|
||||
peer->current_weight = 0;
|
||||
|
||||
@@ -486,6 +533,12 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
|
||||
if (!peer->down) {
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get rr peer2, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (!ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+#endif
|
||||
if (peer->max_fails == 0
|
||||
|| peer->fails < peer->max_fails)
|
||||
{
|
||||
@@ -496,6 +549,9 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
peer->fails = 0;
|
||||
break;
|
||||
}
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ }
|
||||
+#endif
|
||||
|
||||
peer->current_weight = 0;
|
||||
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h
|
||||
index 6d285ab..354cca2 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.h
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.h
|
||||
@@ -28,6 +28,10 @@ typedef struct {
|
||||
ngx_uint_t max_fails;
|
||||
time_t fail_timeout;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_uint_t check_index;
|
||||
+#endif
|
||||
+
|
||||
ngx_uint_t down; /* unsigned down:1; */
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
@ -0,0 +1,241 @@
|
||||
diff --git src/http/modules/ngx_http_upstream_hash_module.c src/http/modules/ngx_http_upstream_hash_module.c
|
||||
--- src/http/modules/ngx_http_upstream_hash_module.c 2016-05-31 15:43:51.000000000 +0200
|
||||
+++ src/http/modules/ngx_http_upstream_hash_module.c 2016-06-22 17:20:19.553955295 +0200
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t hash;
|
||||
@@ -235,6 +238,16 @@
|
||||
goto next;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -535,6 +548,15 @@
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get consistent_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->server.len != server->len
|
||||
|| ngx_strncmp(peer->server.data, server->data, server->len)
|
||||
!= 0)
|
||||
diff --git src/http/modules/ngx_http_upstream_ip_hash_module.c src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
--- src/http/modules/ngx_http_upstream_ip_hash_module.c 2016-05-31 15:43:51.000000000 +0200
|
||||
+++ src/http/modules/ngx_http_upstream_ip_hash_module.c 2016-06-22 17:21:38.465741397 +0200
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
@@ -205,6 +208,15 @@
|
||||
goto next;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get ip_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git src/http/modules/ngx_http_upstream_least_conn_module.c src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
--- src/http/modules/ngx_http_upstream_least_conn_module.c 2016-05-31 15:43:51.000000000 +0200
|
||||
+++ src/http/modules/ngx_http_upstream_least_conn_module.c 2016-06-22 17:23:04.165509237 +0200
|
||||
@@ -9,6 +9,10 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
+
|
||||
|
||||
static ngx_int_t ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
@@ -148,6 +152,16 @@
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -199,6 +213,16 @@
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->conns * best->weight != best->conns * peer->weight) {
|
||||
continue;
|
||||
}
|
||||
|
||||
diff --git src/http/ngx_http_upstream_round_robin.c src/http/ngx_http_upstream_round_robin.c
|
||||
--- src/http/ngx_http_upstream_round_robin.c 2016-05-31 15:43:51.000000000 +0200
|
||||
+++ src/http/ngx_http_upstream_round_robin.c 2016-06-22 17:27:03.200862423 +0200
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
#define ngx_http_upstream_tries(p) ((p)->number \
|
||||
+ ((p)->next ? (p)->next->number : 0))
|
||||
@@ -96,7 +99,14 @@
|
||||
peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
-
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ } else {
|
||||
+ peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
*peerp = &peer[n];
|
||||
peerp = &peer[n].next;
|
||||
n++;
|
||||
@@ -159,7 +169,15 @@
|
||||
peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
-
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
*peerp = &peer[n];
|
||||
peerp = &peer[n].next;
|
||||
n++;
|
||||
@@ -225,6 +243,9 @@
|
||||
peer[i].current_weight = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
*peerp = &peer[i];
|
||||
peerp = &peer[i].next;
|
||||
}
|
||||
@@ -339,6 +360,9 @@
|
||||
peer[0].current_weight = 0;
|
||||
peer[0].max_fails = 1;
|
||||
peer[0].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[0].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
peers->peer = peer;
|
||||
|
||||
} else {
|
||||
@@ -381,6 +405,9 @@
|
||||
peer[i].current_weight = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
*peerp = &peer[i];
|
||||
peerp = &peer[i].next;
|
||||
}
|
||||
@@ -441,6 +468,12 @@
|
||||
goto failed;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto failed;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
rrp->current = peer;
|
||||
|
||||
} else {
|
||||
@@ -542,6 +575,12 @@
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git src/http/ngx_http_upstream_round_robin.h src/http/ngx_http_upstream_round_robin.h
|
||||
--- src/http/ngx_http_upstream_round_robin.h 2016-05-31 15:43:51.000000000 +0200
|
||||
+++ src/http/ngx_http_upstream_round_robin.h 2016-06-22 17:27:47.316743162 +0200
|
||||
@@ -35,6 +35,10 @@
|
||||
ngx_uint_t max_fails;
|
||||
time_t fail_timeout;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_uint_t check_index;
|
||||
+#endif
|
||||
+
|
||||
ngx_uint_t down; /* unsigned down:1; */
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
|
@ -0,0 +1,239 @@
|
||||
diff --git src/http/modules/ngx_http_upstream_hash_module.c src/http/modules/ngx_http_upstream_hash_module.c
|
||||
--- src/http/modules/ngx_http_upstream_hash_module.c
|
||||
+++ src/http/modules/ngx_http_upstream_hash_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t hash;
|
||||
@@ -235,6 +238,16 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto next;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -538,6 +551,15 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get consistent_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->server.len != server->len
|
||||
|| ngx_strncmp(peer->server.data, server->data, server->len)
|
||||
!= 0)
|
||||
diff --git src/http/modules/ngx_http_upstream_ip_hash_module.c src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
--- src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
+++ src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
@@ -205,6 +208,15 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto next;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get ip_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git src/http/modules/ngx_http_upstream_least_conn_module.c src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
--- src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
+++ src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
@@ -9,6 +9,10 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
+
|
||||
|
||||
static ngx_int_t ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
@@ -147,6 +151,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -202,6 +216,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->conns * best->weight != best->conns * peer->weight) {
|
||||
continue;
|
||||
}
|
||||
diff --git src/http/ngx_http_upstream_round_robin.c src/http/ngx_http_upstream_round_robin.c
|
||||
--- src/http/ngx_http_upstream_round_robin.c
|
||||
+++ src/http/ngx_http_upstream_round_robin.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
#define ngx_http_upstream_tries(p) ((p)->number \
|
||||
+ ((p)->next ? (p)->next->number : 0))
|
||||
@@ -97,7 +100,14 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
-
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ } else {
|
||||
+ peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
*peerp = &peer[n];
|
||||
peerp = &peer[n].next;
|
||||
n++;
|
||||
@@ -161,7 +171,15 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
-
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
*peerp = &peer[n];
|
||||
peerp = &peer[n].next;
|
||||
n++;
|
||||
@@ -228,6 +246,9 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[i].max_conns = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
*peerp = &peer[i];
|
||||
peerp = &peer[i].next;
|
||||
}
|
||||
@@ -344,6 +365,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peer[0].max_conns = 0;
|
||||
peer[0].max_fails = 1;
|
||||
peer[0].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[0].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
peers->peer = peer;
|
||||
|
||||
} else {
|
||||
@@ -378,6 +402,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peer[i].max_conns = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
*peerp = &peer[i];
|
||||
peerp = &peer[i].next;
|
||||
}
|
||||
@@ -443,6 +470,12 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto failed;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
rrp->current = peer;
|
||||
|
||||
} else {
|
||||
@@ -537,6 +570,12 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git src/http/ngx_http_upstream_round_robin.h src/http/ngx_http_upstream_round_robin.h
|
||||
--- src/http/ngx_http_upstream_round_robin.h
|
||||
+++ src/http/ngx_http_upstream_round_robin.h
|
||||
@@ -38,6 +38,10 @@ struct ngx_http_upstream_rr_peer_s {
|
||||
ngx_msec_t slow_start;
|
||||
ngx_msec_t start_time;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_uint_t check_index;
|
||||
+#endif
|
||||
+
|
||||
ngx_uint_t down;
|
||||
|
||||
#if (NGX_HTTP_SSL || NGX_COMPAT)
|
@ -0,0 +1,238 @@
|
||||
diff -burN nginx-1.12.1_orig/src/http/modules/ngx_http_upstream_hash_module.c nginx-1.12.1/src/http/modules/ngx_http_upstream_hash_module.c
|
||||
--- nginx-1.12.1_orig/src/http/modules/ngx_http_upstream_hash_module.c 2017-07-11 13:24:08.000000000 +0000
|
||||
+++ nginx-1.12.1/src/http/modules/ngx_http_upstream_hash_module.c 2017-07-13 17:58:44.687213233 +0000
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t hash;
|
||||
@@ -235,6 +238,14 @@
|
||||
goto next;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get hash peer, check_index: %ui", peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -538,6 +549,15 @@
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get consistent_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->server.len != server->len
|
||||
|| ngx_strncmp(peer->server.data, server->data, server->len)
|
||||
!= 0)
|
||||
diff -burN nginx-1.12.1_orig/src/http/modules/ngx_http_upstream_ip_hash_module.c nginx-1.12.1/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
--- nginx-1.12.1_orig/src/http/modules/ngx_http_upstream_ip_hash_module.c 2017-07-11 13:24:08.000000000 +0000
|
||||
+++ nginx-1.12.1/src/http/modules/ngx_http_upstream_ip_hash_module.c 2017-07-13 17:59:48.205692500 +0000
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
@@ -205,6 +208,15 @@
|
||||
goto next;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get ip_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff -burN nginx-1.12.1_orig/src/http/modules/ngx_http_upstream_least_conn_module.c nginx-1.12.1/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
--- nginx-1.12.1_orig/src/http/modules/ngx_http_upstream_least_conn_module.c 2017-07-11 13:24:08.000000000 +0000
|
||||
+++ nginx-1.12.1/src/http/modules/ngx_http_upstream_least_conn_module.c 2017-07-13 18:05:34.417398156 +0000
|
||||
@@ -9,6 +9,10 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
+
|
||||
|
||||
static ngx_int_t ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
@@ -147,6 +151,16 @@
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -202,6 +216,16 @@
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->conns * best->weight != best->conns * peer->weight) {
|
||||
continue;
|
||||
}
|
||||
diff -burN nginx-1.12.1_orig/src/http/ngx_http_upstream_round_robin.c nginx-1.12.1/src/http/ngx_http_upstream_round_robin.c
|
||||
--- nginx-1.12.1_orig/src/http/ngx_http_upstream_round_robin.c 2017-07-11 13:24:09.000000000 +0000
|
||||
+++ nginx-1.12.1/src/http/ngx_http_upstream_round_robin.c 2017-07-13 18:13:00.510764315 +0000
|
||||
@@ -9,6 +9,10 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
+
|
||||
|
||||
#define ngx_http_upstream_tries(p) ((p)->number \
|
||||
+ ((p)->next ? (p)->next->number : 0))
|
||||
@@ -98,6 +102,15 @@
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ } else {
|
||||
+ peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
*peerp = &peer[n];
|
||||
peerp = &peer[n].next;
|
||||
n++;
|
||||
@@ -162,6 +175,16 @@
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
*peerp = &peer[n];
|
||||
peerp = &peer[n].next;
|
||||
n++;
|
||||
@@ -228,6 +251,9 @@
|
||||
peer[i].max_conns = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
*peerp = &peer[i];
|
||||
peerp = &peer[i].next;
|
||||
}
|
||||
@@ -344,6 +370,9 @@
|
||||
peer[0].max_conns = 0;
|
||||
peer[0].max_fails = 1;
|
||||
peer[0].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[0].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
peers->peer = peer;
|
||||
|
||||
} else {
|
||||
@@ -378,6 +407,9 @@
|
||||
peer[i].max_conns = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
*peerp = &peer[i];
|
||||
peerp = &peer[i].next;
|
||||
}
|
||||
@@ -443,6 +475,12 @@
|
||||
goto failed;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto failed;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
rrp->current = peer;
|
||||
|
||||
} else {
|
||||
@@ -537,6 +575,12 @@
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff -burN nginx-1.12.1_orig/src/http/ngx_http_upstream_round_robin.h nginx-1.12.1/src/http/ngx_http_upstream_round_robin.h
|
||||
--- nginx-1.12.1_orig/src/http/ngx_http_upstream_round_robin.h 2017-07-11 13:24:09.000000000 +0000
|
||||
+++ nginx-1.12.1/src/http/ngx_http_upstream_round_robin.h 2017-07-13 18:13:30.254055435 +0000
|
||||
@@ -38,6 +38,10 @@
|
||||
ngx_msec_t slow_start;
|
||||
ngx_msec_t start_time;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_uint_t check_index;
|
||||
+#endif
|
||||
+
|
||||
ngx_uint_t down;
|
||||
|
||||
#if (NGX_HTTP_SSL || NGX_COMPAT)
|
@ -0,0 +1,236 @@
|
||||
diff -burN nginx-1.14.0.orig/src/http/modules/ngx_http_upstream_hash_module.c nginx-1.14.0/src/http/modules/ngx_http_upstream_hash_module.c
|
||||
--- nginx-1.14.0.orig/src/http/modules/ngx_http_upstream_hash_module.c 2018-06-28 21:30:48.891580738 +0000
|
||||
+++ nginx-1.14.0/src/http/modules/ngx_http_upstream_hash_module.c 2018-06-28 21:40:41.801180483 +0000
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t hash;
|
||||
@@ -235,6 +238,14 @@
|
||||
goto next;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get hash peer, check_index: %ui", peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -554,6 +565,15 @@
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get consistent_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->server.len != server->len
|
||||
|| ngx_strncmp(peer->server.data, server->data, server->len)
|
||||
!= 0)
|
||||
diff -burN nginx-1.14.0.orig/src/http/modules/ngx_http_upstream_ip_hash_module.c nginx-1.14.0/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
--- nginx-1.14.0.orig/src/http/modules/ngx_http_upstream_ip_hash_module.c 2018-06-28 21:30:48.891580738 +0000
|
||||
+++ nginx-1.14.0/src/http/modules/ngx_http_upstream_ip_hash_module.c 2018-06-28 21:49:12.608780187 +0000
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
@@ -205,6 +208,15 @@
|
||||
goto next;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get ip_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff -burN nginx-1.14.0.orig/src/http/modules/ngx_http_upstream_least_conn_module.c nginx-1.14.0/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
--- nginx-1.14.0.orig/src/http/modules/ngx_http_upstream_least_conn_module.c 2018-06-28 21:30:48.895580638 +0000
|
||||
+++ nginx-1.14.0/src/http/modules/ngx_http_upstream_least_conn_module.c 2018-06-28 21:50:48.542450442 +0000
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
static ngx_int_t ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
@@ -147,6 +150,16 @@
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -202,6 +215,16 @@
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->conns * best->weight != best->conns * peer->weight) {
|
||||
continue;
|
||||
}
|
||||
diff -burN nginx-1.14.0.orig/src/http/ngx_http_upstream_round_robin.c nginx-1.14.0/src/http/ngx_http_upstream_round_robin.c
|
||||
--- nginx-1.14.0.orig/src/http/ngx_http_upstream_round_robin.c 2018-06-28 21:30:48.887580840 +0000
|
||||
+++ nginx-1.14.0/src/http/ngx_http_upstream_round_robin.c 2018-06-28 21:54:36.492914512 +0000
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
#define ngx_http_upstream_tries(p) ((p)->number \
|
||||
+ ((p)->next ? (p)->next->number : 0))
|
||||
@@ -98,6 +101,15 @@
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ } else {
|
||||
+ peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
*peerp = &peer[n];
|
||||
peerp = &peer[n].next;
|
||||
n++;
|
||||
@@ -162,6 +174,16 @@
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
*peerp = &peer[n];
|
||||
peerp = &peer[n].next;
|
||||
n++;
|
||||
@@ -228,6 +250,9 @@
|
||||
peer[i].max_conns = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
*peerp = &peer[i];
|
||||
peerp = &peer[i].next;
|
||||
}
|
||||
@@ -344,6 +369,9 @@
|
||||
peer[0].max_conns = 0;
|
||||
peer[0].max_fails = 1;
|
||||
peer[0].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[0].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
peers->peer = peer;
|
||||
|
||||
} else {
|
||||
@@ -378,6 +406,9 @@
|
||||
peer[i].max_conns = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
*peerp = &peer[i];
|
||||
peerp = &peer[i].next;
|
||||
}
|
||||
@@ -443,6 +474,12 @@
|
||||
goto failed;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto failed;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
rrp->current = peer;
|
||||
|
||||
} else {
|
||||
@@ -537,6 +574,12 @@
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff -burN nginx-1.14.0.orig/src/http/ngx_http_upstream_round_robin.h nginx-1.14.0/src/http/ngx_http_upstream_round_robin.h
|
||||
--- nginx-1.14.0.orig/src/http/ngx_http_upstream_round_robin.h 2018-06-28 21:30:48.895580638 +0000
|
||||
+++ nginx-1.14.0/src/http/ngx_http_upstream_round_robin.h 2018-06-28 21:55:13.036027376 +0000
|
||||
@@ -38,6 +38,10 @@
|
||||
ngx_msec_t slow_start;
|
||||
ngx_msec_t start_time;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_uint_t check_index;
|
||||
+#endif
|
||||
+
|
||||
ngx_uint_t down;
|
||||
|
||||
#if (NGX_HTTP_SSL || NGX_COMPAT)
|
@ -0,0 +1,241 @@
|
||||
diff --git a/src/http/modules/ngx_http_upstream_hash_module.c b/src/http/modules/ngx_http_upstream_hash_module.c
|
||||
index 6c247b5..1ae9cce 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_hash_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_hash_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t hash;
|
||||
@@ -238,6 +241,14 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto next;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get hash peer, check_index: %ui", peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -560,6 +571,15 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get consistent_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->server.len != server->len
|
||||
|| ngx_strncmp(peer->server.data, server->data, server->len)
|
||||
!= 0)
|
||||
diff --git a/src/http/modules/ngx_http_upstream_ip_hash_module.c b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
index 1fa01d9..366aca9 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
@@ -208,6 +211,15 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto next;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get ip_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git a/src/http/modules/ngx_http_upstream_least_conn_module.c b/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
index ebe0627..3525035 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
static ngx_int_t ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
@@ -147,6 +150,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -202,6 +215,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->conns * best->weight != best->conns * peer->weight) {
|
||||
continue;
|
||||
}
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c
|
||||
index f72de3e..78b3342 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.c
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
#define ngx_http_upstream_tries(p) ((p)->number \
|
||||
+ ((p)->next ? (p)->next->number : 0))
|
||||
@@ -98,6 +101,15 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ } else {
|
||||
+ peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
*peerp = &peer[n];
|
||||
peerp = &peer[n].next;
|
||||
n++;
|
||||
@@ -162,6 +174,16 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
*peerp = &peer[n];
|
||||
peerp = &peer[n].next;
|
||||
n++;
|
||||
@@ -228,6 +250,9 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[i].max_conns = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
*peerp = &peer[i];
|
||||
peerp = &peer[i].next;
|
||||
}
|
||||
@@ -344,6 +369,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peer[0].max_conns = 0;
|
||||
peer[0].max_fails = 1;
|
||||
peer[0].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[0].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
peers->peer = peer;
|
||||
|
||||
} else {
|
||||
@@ -378,6 +406,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peer[i].max_conns = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
*peerp = &peer[i];
|
||||
peerp = &peer[i].next;
|
||||
}
|
||||
@@ -443,6 +474,12 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto failed;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
rrp->current = peer;
|
||||
|
||||
} else {
|
||||
@@ -537,6 +574,12 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h
|
||||
index 45f258d..dee91d0 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.h
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.h
|
||||
@@ -38,6 +38,10 @@ struct ngx_http_upstream_rr_peer_s {
|
||||
ngx_msec_t slow_start;
|
||||
ngx_msec_t start_time;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_uint_t check_index;
|
||||
+#endif
|
||||
+
|
||||
ngx_uint_t down;
|
||||
|
||||
#if (NGX_HTTP_SSL || NGX_COMPAT)
|
@ -0,0 +1,160 @@
|
||||
diff --git a/src/http/modules/ngx_http_upstream_ip_hash_module.c b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
index 100ea34..642b01b 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
@@ -9,6 +9,10 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
+
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
@@ -182,6 +186,12 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
|
||||
if (!peer->down) {
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get ip_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (!ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+#endif
|
||||
if (peer->max_fails == 0 || peer->fails < peer->max_fails) {
|
||||
break;
|
||||
}
|
||||
@@ -190,6 +200,9 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
peer->checked = now;
|
||||
break;
|
||||
}
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ }
|
||||
+#endif
|
||||
}
|
||||
|
||||
iphp->rrp.tried[n] |= m;
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c
|
||||
index 214de7b..309725b 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.c
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
static ngx_int_t ngx_http_upstream_cmp_servers(const void *one,
|
||||
const void *two);
|
||||
@@ -83,7 +86,17 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peers->peer[n].weight = server[i].weight;
|
||||
peers->peer[n].effective_weight = server[i].weight;
|
||||
peers->peer[n].current_weight = 0;
|
||||
- n++;
|
||||
+
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peers->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ peers->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+ n++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,6 +150,17 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
backup->peer[n].max_fails = server[i].max_fails;
|
||||
backup->peer[n].fail_timeout = server[i].fail_timeout;
|
||||
backup->peer[n].down = server[i].down;
|
||||
+
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ backup->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ backup->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
n++;
|
||||
}
|
||||
}
|
||||
@@ -196,6 +220,9 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peers->peer[i].current_weight = 0;
|
||||
peers->peer[i].max_fails = 1;
|
||||
peers->peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
}
|
||||
|
||||
us->peer.data = peers;
|
||||
@@ -313,6 +340,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peers->peer[0].current_weight = 0;
|
||||
peers->peer[0].max_fails = 1;
|
||||
peers->peer[0].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[0].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
|
||||
} else {
|
||||
|
||||
@@ -346,6 +376,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peers->peer[i].current_weight = 0;
|
||||
peers->peer[i].max_fails = 1;
|
||||
peers->peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,7 +452,11 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
|
||||
if (rrp->peers->single) {
|
||||
peer = &rrp->peers->peer[0];
|
||||
-
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ return NGX_BUSY;
|
||||
+ }
|
||||
+#endif
|
||||
} else {
|
||||
|
||||
/* there are several peers */
|
||||
@@ -517,6 +554,12 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h
|
||||
index 4de3cae..164867b 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.h
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.h
|
||||
@@ -30,6 +30,10 @@ typedef struct {
|
||||
ngx_uint_t max_fails;
|
||||
time_t fail_timeout;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_uint_t check_index;
|
||||
+#endif
|
||||
+
|
||||
ngx_uint_t down; /* unsigned down:1; */
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
@ -0,0 +1,209 @@
|
||||
diff --git a/src/http/modules/ngx_http_upstream_ip_hash_module.c b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
index 89ccc2b..a552044 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
@@ -9,6 +9,10 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
+
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
@@ -208,6 +212,12 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
|
||||
if (!peer->down) {
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get ip_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (!ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+#endif
|
||||
if (peer->max_fails == 0 || peer->fails < peer->max_fails) {
|
||||
break;
|
||||
}
|
||||
@@ -216,6 +226,9 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
peer->checked = now;
|
||||
break;
|
||||
}
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ }
|
||||
+#endif
|
||||
}
|
||||
|
||||
iphp->rrp.tried[n] |= m;
|
||||
diff --git a/src/http/modules/ngx_http_upstream_least_conn_module.c b/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
index 50e68b2..f2f32cc 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
@@ -9,6 +9,10 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
+
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t *conns;
|
||||
@@ -203,6 +207,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -256,6 +270,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (lcp->conns[i] * best->weight != lcp->conns[p] * peer->weight) {
|
||||
continue;
|
||||
}
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c
|
||||
index c4998fc..f3e9378 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.c
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
static ngx_int_t ngx_http_upstream_cmp_servers(const void *one,
|
||||
const void *two);
|
||||
@@ -87,7 +90,17 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peers->peer[n].weight = server[i].weight;
|
||||
peers->peer[n].effective_weight = server[i].weight;
|
||||
peers->peer[n].current_weight = 0;
|
||||
- n++;
|
||||
+
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peers->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ peers->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+ n++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +158,17 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
backup->peer[n].max_fails = server[i].max_fails;
|
||||
backup->peer[n].fail_timeout = server[i].fail_timeout;
|
||||
backup->peer[n].down = server[i].down;
|
||||
+
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ backup->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ backup->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
n++;
|
||||
}
|
||||
}
|
||||
@@ -206,6 +230,9 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peers->peer[i].current_weight = 0;
|
||||
peers->peer[i].max_fails = 1;
|
||||
peers->peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
}
|
||||
|
||||
us->peer.data = peers;
|
||||
@@ -323,6 +350,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peers->peer[0].current_weight = 0;
|
||||
peers->peer[0].max_fails = 1;
|
||||
peers->peer[0].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[0].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
|
||||
} else {
|
||||
|
||||
@@ -356,6 +386,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peers->peer[i].current_weight = 0;
|
||||
peers->peer[i].max_fails = 1;
|
||||
peers->peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,7 +462,11 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
|
||||
if (rrp->peers->single) {
|
||||
peer = &rrp->peers->peer[0];
|
||||
-
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ return NGX_BUSY;
|
||||
+ }
|
||||
+#endif
|
||||
} else {
|
||||
|
||||
/* there are several peers */
|
||||
@@ -527,6 +564,12 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h
|
||||
index 3f8cbf8..1613168 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.h
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.h
|
||||
@@ -30,6 +30,10 @@ typedef struct {
|
||||
ngx_uint_t max_fails;
|
||||
time_t fail_timeout;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_uint_t check_index;
|
||||
+#endif
|
||||
+
|
||||
ngx_uint_t down; /* unsigned down:1; */
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
@ -0,0 +1,209 @@
|
||||
diff --git a/src/http/modules/ngx_http_upstream_ip_hash_module.c b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
index 89ccc2b..a552044 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
@@ -9,6 +9,10 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
+
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
@@ -208,6 +212,12 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
|
||||
if (!peer->down) {
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get ip_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (!ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+#endif
|
||||
if (peer->max_fails == 0 || peer->fails < peer->max_fails) {
|
||||
break;
|
||||
}
|
||||
@@ -216,6 +226,9 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
peer->checked = now;
|
||||
break;
|
||||
}
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ }
|
||||
+#endif
|
||||
}
|
||||
|
||||
iphp->rrp.tried[n] |= m;
|
||||
diff --git a/src/http/modules/ngx_http_upstream_least_conn_module.c b/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
index 21156ae..c57393d 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
@@ -9,6 +9,10 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
+
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t *conns;
|
||||
@@ -203,6 +207,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -256,6 +270,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (lcp->conns[i] * best->weight != lcp->conns[p] * peer->weight) {
|
||||
continue;
|
||||
}
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c
|
||||
index 4b78cff..f077b46 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.c
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
static ngx_int_t ngx_http_upstream_cmp_servers(const void *one,
|
||||
const void *two);
|
||||
@@ -87,7 +90,17 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peers->peer[n].weight = server[i].weight;
|
||||
peers->peer[n].effective_weight = server[i].weight;
|
||||
peers->peer[n].current_weight = 0;
|
||||
- n++;
|
||||
+
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peers->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ peers->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+ n++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +158,17 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
backup->peer[n].max_fails = server[i].max_fails;
|
||||
backup->peer[n].fail_timeout = server[i].fail_timeout;
|
||||
backup->peer[n].down = server[i].down;
|
||||
+
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ backup->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ backup->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
n++;
|
||||
}
|
||||
}
|
||||
@@ -206,6 +230,9 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peers->peer[i].current_weight = 0;
|
||||
peers->peer[i].max_fails = 1;
|
||||
peers->peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
}
|
||||
|
||||
us->peer.data = peers;
|
||||
@@ -323,6 +350,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peers->peer[0].current_weight = 0;
|
||||
peers->peer[0].max_fails = 1;
|
||||
peers->peer[0].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[0].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
|
||||
} else {
|
||||
|
||||
@@ -356,6 +386,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peers->peer[i].current_weight = 0;
|
||||
peers->peer[i].max_fails = 1;
|
||||
peers->peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,6 +467,12 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto failed;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
} else {
|
||||
|
||||
/* there are several peers */
|
||||
@@ -531,6 +570,12 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h
|
||||
index 3f8cbf8..1613168 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.h
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.h
|
||||
@@ -30,6 +30,10 @@ typedef struct {
|
||||
ngx_uint_t max_fails;
|
||||
time_t fail_timeout;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_uint_t check_index;
|
||||
+#endif
|
||||
+
|
||||
ngx_uint_t down; /* unsigned down:1; */
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
@ -0,0 +1,241 @@
|
||||
diff --git a/src/http/modules/ngx_http_upstream_hash_module.c b/src/http/modules/ngx_http_upstream_hash_module.c
|
||||
index e741eb23..d7d288d9 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_hash_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_hash_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t hash;
|
||||
@@ -238,6 +241,14 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto next;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get hash peer, check_index: %ui", peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -560,6 +571,15 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get consistent_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->server.len != server->len
|
||||
|| ngx_strncmp(peer->server.data, server->data, server->len)
|
||||
!= 0)
|
||||
diff --git a/src/http/modules/ngx_http_upstream_ip_hash_module.c b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
index 1fa01d95..366aca9a 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
@@ -208,6 +211,15 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto next;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get ip_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git a/src/http/modules/ngx_http_upstream_least_conn_module.c b/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
index ebe06276..35250354 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
static ngx_int_t ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
@@ -147,6 +150,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -202,6 +215,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->conns * best->weight != best->conns * peer->weight) {
|
||||
continue;
|
||||
}
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c
|
||||
index 1f15fae5..d507a0e3 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.c
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
#define ngx_http_upstream_tries(p) ((p)->tries \
|
||||
+ ((p)->next ? (p)->next->tries : 0))
|
||||
@@ -104,6 +107,15 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ } else {
|
||||
+ peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
*peerp = &peer[n];
|
||||
peerp = &peer[n].next;
|
||||
n++;
|
||||
@@ -174,6 +186,16 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
*peerp = &peer[n];
|
||||
peerp = &peer[n].next;
|
||||
n++;
|
||||
@@ -241,6 +263,9 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[i].max_conns = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
*peerp = &peer[i];
|
||||
peerp = &peer[i].next;
|
||||
}
|
||||
@@ -358,6 +383,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peer[0].max_conns = 0;
|
||||
peer[0].max_fails = 1;
|
||||
peer[0].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[0].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
peers->peer = peer;
|
||||
|
||||
} else {
|
||||
@@ -392,6 +420,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peer[i].max_conns = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
*peerp = &peer[i];
|
||||
peerp = &peer[i].next;
|
||||
}
|
||||
@@ -457,6 +488,12 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto failed;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
rrp->current = peer;
|
||||
|
||||
} else {
|
||||
@@ -551,6 +588,12 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h
|
||||
index 922ceaa0..14d8ad86 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.h
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.h
|
||||
@@ -38,6 +38,10 @@ struct ngx_http_upstream_rr_peer_s {
|
||||
ngx_msec_t slow_start;
|
||||
ngx_msec_t start_time;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_uint_t check_index;
|
||||
+#endif
|
||||
+
|
||||
ngx_uint_t down;
|
||||
|
||||
#if (NGX_HTTP_SSL || NGX_COMPAT)
|
@ -0,0 +1,198 @@
|
||||
diff --git a/src/http/modules/ngx_http_upstream_ip_hash_module.c b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
index 041883f..b1bc7d0 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
@@ -9,6 +9,10 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
+
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
@@ -212,6 +216,15 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto next_try;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get ip_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next_try;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git a/src/http/modules/ngx_http_upstream_least_conn_module.c b/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
index dbef95d..dc9b518 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
@@ -9,6 +9,10 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
+
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t *conns;
|
||||
@@ -203,6 +207,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -256,6 +270,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (lcp->conns[i] * best->weight != lcp->conns[p] * peer->weight) {
|
||||
continue;
|
||||
}
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c
|
||||
index 85ff558..2fe9bb6 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.c
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
static ngx_http_upstream_rr_peer_t *ngx_http_upstream_get_peer(
|
||||
ngx_http_upstream_rr_peer_data_t *rrp);
|
||||
@@ -85,6 +88,14 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peers->peer[n].max_fails = server[i].max_fails;
|
||||
peers->peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peers->peer[n].down = server[i].down;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peers->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ } else {
|
||||
+ peers->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
n++;
|
||||
}
|
||||
}
|
||||
@@ -139,6 +150,17 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
backup->peer[n].max_fails = server[i].max_fails;
|
||||
backup->peer[n].fail_timeout = server[i].fail_timeout;
|
||||
backup->peer[n].down = server[i].down;
|
||||
+
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ backup->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ backup->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
n++;
|
||||
}
|
||||
}
|
||||
@@ -196,6 +218,9 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peers->peer[i].current_weight = 0;
|
||||
peers->peer[i].max_fails = 1;
|
||||
peers->peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
}
|
||||
|
||||
us->peer.data = peers;
|
||||
@@ -302,6 +327,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peers->peer[0].current_weight = 0;
|
||||
peers->peer[0].max_fails = 1;
|
||||
peers->peer[0].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[0].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
|
||||
} else {
|
||||
|
||||
@@ -342,6 +370,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peers->peer[i].current_weight = 0;
|
||||
peers->peer[i].max_fails = 1;
|
||||
peers->peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,6 +430,12 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto failed;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
} else {
|
||||
|
||||
/* there are several peers */
|
||||
@@ -498,6 +535,12 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h
|
||||
index ea90ab9..a6fb33f 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.h
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.h
|
||||
@@ -30,6 +30,10 @@ typedef struct {
|
||||
ngx_uint_t max_fails;
|
||||
time_t fail_timeout;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_uint_t check_index;
|
||||
+#endif
|
||||
+
|
||||
ngx_uint_t down; /* unsigned down:1; */
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
@ -0,0 +1,239 @@
|
||||
diff --git a/src/http/modules/ngx_http_upstream_hash_module.c b/src/http/modules/ngx_http_upstream_hash_module.c
|
||||
index 777e180..e302f52 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_hash_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_hash_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t hash;
|
||||
@@ -240,6 +243,14 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto next;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next;
|
||||
+ }
|
||||
+#endif
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -506,6 +517,14 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get consistent_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
if (peer->server.len != server->len
|
||||
|| ngx_strncmp(peer->server.data, server->data, server->len)
|
||||
!= 0)
|
||||
diff --git a/src/http/modules/ngx_http_upstream_ip_hash_module.c b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
index 148d73a..913e395 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
@@ -212,6 +215,15 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto next_try;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get ip_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next_try;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git a/src/http/modules/ngx_http_upstream_least_conn_module.c b/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
index dbef95d..bbabb68 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t *conns;
|
||||
@@ -203,6 +206,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -256,6 +269,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (lcp->conns[i] * best->weight != lcp->conns[p] * peer->weight) {
|
||||
continue;
|
||||
}
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c
|
||||
index 37c835c..54aa44d 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.c
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
static ngx_http_upstream_rr_peer_t *ngx_http_upstream_get_peer(
|
||||
ngx_http_upstream_rr_peer_data_t *rrp);
|
||||
@@ -88,6 +91,14 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peers->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ } else {
|
||||
+ peers->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
n++;
|
||||
}
|
||||
}
|
||||
@@ -144,6 +155,15 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ backup->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ backup->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
n++;
|
||||
}
|
||||
}
|
||||
@@ -203,6 +223,9 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[i].current_weight = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
}
|
||||
|
||||
us->peer.data = peers;
|
||||
@@ -312,7 +335,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peer[0].current_weight = 0;
|
||||
peer[0].max_fails = 1;
|
||||
peer[0].fail_timeout = 10;
|
||||
-
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[0].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
} else {
|
||||
|
||||
for (i = 0; i < ur->naddrs; i++) {
|
||||
@@ -352,6 +377,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peer[i].current_weight = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,6 +439,12 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto failed;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
} else {
|
||||
|
||||
/* there are several peers */
|
||||
@@ -508,6 +542,12 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h
|
||||
index 9db82a6..6e19a65 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.h
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.h
|
||||
@@ -31,6 +31,10 @@ typedef struct {
|
||||
ngx_uint_t max_fails;
|
||||
time_t fail_timeout;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_uint_t check_index;
|
||||
+#endif
|
||||
+
|
||||
ngx_uint_t down; /* unsigned down:1; */
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
@ -0,0 +1,241 @@
|
||||
diff --git a/src/http/modules/ngx_http_upstream_hash_module.c b/src/http/modules/ngx_http_upstream_hash_module.c
|
||||
index 777e180..b6b7830 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_hash_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_hash_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t hash;
|
||||
@@ -240,6 +243,15 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto next;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -506,6 +518,15 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get consistent_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->server.len != server->len
|
||||
|| ngx_strncmp(peer->server.data, server->data, server->len)
|
||||
!= 0)
|
||||
diff --git a/src/http/modules/ngx_http_upstream_ip_hash_module.c b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
index 148d73a..913e395 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
@@ -212,6 +215,15 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto next_try;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get ip_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next_try;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git a/src/http/modules/ngx_http_upstream_least_conn_module.c b/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
index 623bc9b..a223839 100644
|
||||
--- a/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
+++ b/src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t *conns;
|
||||
@@ -203,6 +206,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -256,6 +269,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (lcp->conns[i] * best->weight != lcp->conns[p] * peer->weight) {
|
||||
continue;
|
||||
}
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c
|
||||
index 2d0649b..b9789eb 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.c
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
#define ngx_http_upstream_tries(p) ((p)->number \
|
||||
+ ((p)->next ? (p)->next->number : 0))
|
||||
@@ -92,6 +95,14 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peers->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ } else {
|
||||
+ peers->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
n++;
|
||||
}
|
||||
}
|
||||
@@ -148,6 +159,15 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ backup->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ backup->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
n++;
|
||||
}
|
||||
}
|
||||
@@ -207,6 +227,9 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[i].current_weight = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
}
|
||||
|
||||
us->peer.data = peers;
|
||||
@@ -316,7 +339,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peer[0].current_weight = 0;
|
||||
peer[0].max_fails = 1;
|
||||
peer[0].fail_timeout = 10;
|
||||
-
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[0].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
} else {
|
||||
|
||||
for (i = 0; i < ur->naddrs; i++) {
|
||||
@@ -356,6 +381,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peer[i].current_weight = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,6 +443,12 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto failed;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
} else {
|
||||
|
||||
/* there are several peers */
|
||||
@@ -507,6 +541,12 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h
|
||||
index 9db82a6..6e19a65 100644
|
||||
--- a/src/http/ngx_http_upstream_round_robin.h
|
||||
+++ b/src/http/ngx_http_upstream_round_robin.h
|
||||
@@ -31,6 +31,10 @@ typedef struct {
|
||||
ngx_uint_t max_fails;
|
||||
time_t fail_timeout;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_uint_t check_index;
|
||||
+#endif
|
||||
+
|
||||
ngx_uint_t down; /* unsigned down:1; */
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
@ -0,0 +1,242 @@
|
||||
diff --git src/http/modules/ngx_http_upstream_hash_module.c src/http/modules/ngx_http_upstream_hash_module.c
|
||||
index 1e2e05c..44a72e2 100644
|
||||
--- src/http/modules/ngx_http_upstream_hash_module.c
|
||||
+++ src/http/modules/ngx_http_upstream_hash_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t hash;
|
||||
@@ -235,6 +238,15 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto next;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -535,6 +547,15 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get consistent_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->server.len != server->len
|
||||
|| ngx_strncmp(peer->server.data, server->data, server->len)
|
||||
!= 0)
|
||||
diff --git src/http/modules/ngx_http_upstream_ip_hash_module.c src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
index 401b58e..ba656bd 100644
|
||||
--- src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
+++ src/http/modules/ngx_http_upstream_ip_hash_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
@@ -205,6 +208,15 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto next;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get ip_hash peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto next;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git src/http/modules/ngx_http_upstream_least_conn_module.c src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
index 92951bd..48aca2c 100644
|
||||
--- src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
+++ src/http/modules/ngx_http_upstream_least_conn_module.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
static ngx_int_t ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
@@ -148,6 +151,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
@@ -199,6 +212,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get least_conn peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->conns * best->weight != best->conns * peer->weight) {
|
||||
continue;
|
||||
}
|
||||
diff --git src/http/ngx_http_upstream_round_robin.c src/http/ngx_http_upstream_round_robin.c
|
||||
index d6ae33b..416572a 100644
|
||||
--- src/http/ngx_http_upstream_round_robin.c
|
||||
+++ src/http/ngx_http_upstream_round_robin.c
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
|
||||
#define ngx_http_upstream_tries(p) ((p)->number \
|
||||
+ ((p)->next ? (p)->next->number : 0))
|
||||
@@ -96,7 +99,14 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
-
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ } else {
|
||||
+ peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
*peerp = &peer[n];
|
||||
peerp = &peer[n].next;
|
||||
n++;
|
||||
@@ -159,7 +169,15 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
-
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
*peerp = &peer[n];
|
||||
peerp = &peer[n].next;
|
||||
n++;
|
||||
@@ -225,6 +243,9 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
peer[i].current_weight = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
*peerp = &peer[i];
|
||||
peerp = &peer[i].next;
|
||||
}
|
||||
@@ -339,6 +360,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peer[0].current_weight = 0;
|
||||
peer[0].max_fails = 1;
|
||||
peer[0].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[0].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
peers->peer = peer;
|
||||
|
||||
} else {
|
||||
@@ -381,6 +405,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
peer[i].current_weight = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
*peerp = &peer[i];
|
||||
peerp = &peer[i].next;
|
||||
}
|
||||
@@ -441,6 +468,12 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ goto failed;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
rrp->current = peer;
|
||||
|
||||
} else {
|
||||
@@ -542,6 +575,12 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
|
||||
continue;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
diff --git src/http/ngx_http_upstream_round_robin.h src/http/ngx_http_upstream_round_robin.h
|
||||
index f2c573f..75e0ed6 100644
|
||||
--- src/http/ngx_http_upstream_round_robin.h
|
||||
+++ src/http/ngx_http_upstream_round_robin.h
|
||||
@@ -35,6 +35,10 @@ struct ngx_http_upstream_rr_peer_s {
|
||||
ngx_uint_t max_fails;
|
||||
time_t fail_timeout;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_uint_t check_index;
|
||||
+#endif
|
||||
+
|
||||
ngx_uint_t down; /* unsigned down:1; */
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
@ -0,0 +1,24 @@
|
||||
ngx_feature="ngx_http_upstream_check_module"
|
||||
ngx_feature_name=
|
||||
ngx_feature_run=no
|
||||
ngx_feature_incs=
|
||||
ngx_feature_libs=""
|
||||
ngx_feature_path="$ngx_addon_dir"
|
||||
ngx_feature_deps="$ngx_addon_dir/ngx_http_upstream_check_module.h"
|
||||
ngx_check_src="$ngx_addon_dir/ngx_http_upstream_check_module.c"
|
||||
ngx_feature_test="int a;"
|
||||
. auto/feature
|
||||
|
||||
if [ $ngx_found = yes ]; then
|
||||
have=NGX_HTTP_UPSTREAM_CHECK . auto/have
|
||||
CORE_INCS="$CORE_INCS $ngx_feature_path"
|
||||
ngx_addon_name=ngx_http_upstream_check_module
|
||||
HTTP_MODULES="$HTTP_MODULES ngx_http_upstream_check_module"
|
||||
NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_feature_deps"
|
||||
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_check_src"
|
||||
else
|
||||
cat << END
|
||||
$0: error: the ngx_http_upstream_check_module addon error.
|
||||
END
|
||||
exit 1
|
||||
fi
|
@ -0,0 +1,347 @@
|
||||
Name
|
||||
nginx_http_upstream_check_module - support upstream health check with
|
||||
Nginx
|
||||
|
||||
Synopsis
|
||||
http {
|
||||
|
||||
upstream cluster {
|
||||
|
||||
# simple round-robin
|
||||
server 192.168.0.1:80;
|
||||
server 192.168.0.2:80;
|
||||
|
||||
check interval=5000 rise=1 fall=3 timeout=4000;
|
||||
|
||||
#check interval=3000 rise=2 fall=5 timeout=1000 type=ssl_hello;
|
||||
|
||||
#check interval=3000 rise=2 fall=5 timeout=1000 type=http;
|
||||
#check_http_send "HEAD / HTTP/1.0\r\n\r\n";
|
||||
#check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
location / {
|
||||
proxy_pass http://cluster;
|
||||
}
|
||||
|
||||
location /status {
|
||||
check_status;
|
||||
|
||||
access_log off;
|
||||
allow SOME.IP.ADD.RESS;
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Description
|
||||
Add the support of health check with the upstream servers.
|
||||
|
||||
Directives
|
||||
check
|
||||
syntax: *check interval=milliseconds [fall=count] [rise=count]
|
||||
[timeout=milliseconds] [default_down=true|false]
|
||||
[type=tcp|http|ssl_hello|mysql|ajp|fastcgi]*
|
||||
|
||||
default: *none, if parameters omitted, default parameters are
|
||||
interval=30000 fall=5 rise=2 timeout=1000 default_down=true type=tcp*
|
||||
|
||||
context: *upstream*
|
||||
|
||||
description: Add the health check for the upstream servers.
|
||||
|
||||
The parameters' meanings are:
|
||||
|
||||
* *interval*: the check request's interval time.
|
||||
|
||||
* *fall*(fall_count): After fall_count check failures, the server is
|
||||
marked down.
|
||||
|
||||
* *rise*(rise_count): After rise_count check success, the server is
|
||||
marked up.
|
||||
|
||||
* *timeout*: the check request's timeout.
|
||||
|
||||
* *default_down*: set initial state of backend server, default is
|
||||
down.
|
||||
|
||||
* *port*: specify the check port in the backend servers. It can be
|
||||
different with the original servers port. Default the port is 0 and
|
||||
it means the same as the original backend server.
|
||||
|
||||
* *type*: the check protocol type:
|
||||
|
||||
1. *tcp* is a simple tcp socket connect and peek one byte.
|
||||
|
||||
2. *ssl_hello* sends a client ssl hello packet and receives the
|
||||
server ssl hello packet.
|
||||
|
||||
3. *http* sends a http request packet, receives and parses the http
|
||||
response to diagnose if the upstream server is alive.
|
||||
|
||||
4. *mysql* connects to the mysql server, receives the greeting
|
||||
response to diagnose if the upstream server is alive.
|
||||
|
||||
5. *ajp* sends a AJP Cping packet, receives and parses the AJP
|
||||
Cpong response to diagnose if the upstream server is alive.
|
||||
|
||||
6. *fastcgi* send a fastcgi request, receives and parses the
|
||||
fastcgi response to diagnose if the upstream server is alive.
|
||||
|
||||
check_http_send
|
||||
syntax: *check_http_send http_packet*
|
||||
|
||||
default: *"GET / HTTP/1.0\r\n\r\n"*
|
||||
|
||||
context: *upstream*
|
||||
|
||||
description: If you set the check type is http, then the check function
|
||||
will sends this http packet to check the upstream server.
|
||||
|
||||
check_http_expect_alive
|
||||
syntax: *check_http_expect_alive [ http_2xx | http_3xx | http_4xx |
|
||||
http_5xx ]*
|
||||
|
||||
default: *http_2xx | http_3xx*
|
||||
|
||||
context: *upstream*
|
||||
|
||||
description: These status codes indicate the upstream server's http
|
||||
response is ok, the backend is alive.
|
||||
|
||||
check_keepalive_requests
|
||||
syntax: *check_keepalive_requests num*
|
||||
|
||||
default: *check_keepalive_requests 1*
|
||||
|
||||
context: *upstream*
|
||||
|
||||
description: The directive specifies the number of requests sent on a
|
||||
connection, the default vaule 1 indicates that nginx will certainly
|
||||
close the connection after a request.
|
||||
|
||||
check_fastcgi_param
|
||||
Syntax: *check_fastcgi_params parameter value*
|
||||
|
||||
default: see below
|
||||
|
||||
context: *upstream*
|
||||
|
||||
description: If you set the check type is fastcgi, then the check
|
||||
function will sends this fastcgi headers to check the upstream server.
|
||||
The default directive looks like:
|
||||
|
||||
check_fastcgi_param "REQUEST_METHOD" "GET";
|
||||
check_fastcgi_param "REQUEST_URI" "/";
|
||||
check_fastcgi_param "SCRIPT_FILENAME" "index.php";
|
||||
|
||||
check_shm_size
|
||||
syntax: *check_shm_size size*
|
||||
|
||||
default: *1M*
|
||||
|
||||
context: *http*
|
||||
|
||||
description: Default size is one megabytes. If you check thousands of
|
||||
servers, the shared memory for health check may be not enough, you can
|
||||
enlarge it with this directive.
|
||||
|
||||
check_status
|
||||
syntax: *check_status [html|csv|json]*
|
||||
|
||||
default: *none*
|
||||
|
||||
context: *location*
|
||||
|
||||
description: Display the health checking servers' status by HTTP. This
|
||||
directive should be set in the http block.
|
||||
|
||||
You can specify the default display format. The formats can be `html`,
|
||||
`csv` or `json`. The default type is `html`. It also supports to specify
|
||||
the format by the request argument. Suppose your `check_status` location
|
||||
is '/status', the argument of `format` can change the display page's
|
||||
format. You can do like this:
|
||||
|
||||
/status?format=html
|
||||
/status?format=csv
|
||||
/status?format=json
|
||||
|
||||
At present, you can fetch the list of servers with the same status by
|
||||
the argument of `status`. For example:
|
||||
|
||||
/status?format=html&status=down
|
||||
/status?format=csv&status=up
|
||||
|
||||
Below it's the sample html page:
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<title>Nginx http upstream check status</title>
|
||||
<h1>Nginx http upstream check status</h1>
|
||||
<h2>Check upstream server number: 1, generation: 3</h2>
|
||||
<th>Index</th>
|
||||
<th>Upstream</th>
|
||||
<th>Name</th>
|
||||
<th>Status</th>
|
||||
<th>Rise counts</th>
|
||||
<th>Fall counts</th>
|
||||
<th>Check type</th>
|
||||
<th>Check port</th>
|
||||
<td>0</td>
|
||||
<td>backend</td>
|
||||
<td>106.187.48.116:80</td>
|
||||
<td>up</td>
|
||||
<td>39</td>
|
||||
<td>0</td>
|
||||
<td>http</td>
|
||||
<td>80</td>
|
||||
|
||||
Below it's the sample of csv page:
|
||||
|
||||
0,backend,106.187.48.116:80,up,46,0,http,80
|
||||
|
||||
Below it's the sample of json page:
|
||||
|
||||
{"servers": {
|
||||
"total": 1,
|
||||
"generation": 3,
|
||||
"server": [
|
||||
{"index": 0, "upstream": "backend", "name": "106.187.48.116:80", "status": "up", "rise": 58, "fall": 0, "type": "http", "port": 80}
|
||||
]
|
||||
}}
|
||||
|
||||
Installation
|
||||
Download the latest version of the release tarball of this module from
|
||||
github (<http://github.com/yaoweibin/nginx_upstream_check_module>)
|
||||
|
||||
Grab the nginx source code from nginx.org (<http://nginx.org/>), for
|
||||
example, the version 1.0.14 (see nginx compatibility), and then build
|
||||
the source with this module:
|
||||
|
||||
$ wget 'http://nginx.org/download/nginx-1.0.14.tar.gz'
|
||||
$ tar -xzvf nginx-1.0.14.tar.gz
|
||||
$ cd nginx-1.0.14/
|
||||
$ patch -p1 < /path/to/nginx_http_upstream_check_module/check.patch
|
||||
|
||||
$ ./configure --add-module=/path/to/nginx_http_upstream_check_module
|
||||
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
Note
|
||||
If you use nginx-1.2.1 or nginx-1.3.0, the nginx upstream round robin
|
||||
module changed greatly. You should use the patch named
|
||||
'check_1.2.1.patch'.
|
||||
|
||||
If you use nginx-1.2.2+ or nginx-1.3.1+, It added the upstream
|
||||
least_conn module. You should use the patch named 'check_1.2.2+.patch'.
|
||||
|
||||
If you use nginx-1.2.6+ or nginx-1.3.9+, It adjusted the round robin
|
||||
module. You should use the patch named 'check_1.2.6+.patch'.
|
||||
|
||||
If you use nginx-1.5.12+, You should use the patch named
|
||||
'check_1.5.12+.patch'.
|
||||
|
||||
If you use nginx-1.7.2+, You should use the patch named
|
||||
'check_1.7.2+.patch'.
|
||||
|
||||
The patch just adds the support for the official Round-Robin, Ip_hash
|
||||
and least_conn upstream module. But it's easy to expand my module to
|
||||
other upstream modules. See the patch for detail.
|
||||
|
||||
If you want to add the support for upstream fair module, you can do it
|
||||
like this:
|
||||
|
||||
$ git clone git://github.com/gnosek/nginx-upstream-fair.git
|
||||
$ cd nginx-upstream-fair
|
||||
$ patch -p2 < /path/to/nginx_http_upstream_check_module/upstream_fair.patch
|
||||
$ cd /path/to/nginx-1.0.14
|
||||
$ ./configure --add-module=/path/to/nginx_http_upstream_check_module --add-module=/path/to/nginx-upstream-fair-module
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
If you want to add the support for nginx sticky module, you can do it
|
||||
like this:
|
||||
|
||||
$ svn checkout http://nginx-sticky-module.googlecode.com/svn/trunk/ nginx-sticky-module
|
||||
$ cd nginx-sticky-module
|
||||
$ patch -p0 < /path/to/nginx_http_upstream_check_module/nginx-sticky-module.patch
|
||||
$ cd /path/to/nginx-1.0.14
|
||||
$ ./configure --add-module=/path/to/nginx_http_upstream_check_module --add-module=/path/to/nginx-sticky-module
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
Note that, the nginx-sticky-module also needs the original check.patch.
|
||||
|
||||
Compatibility
|
||||
* The module version 0.1.5 should be compatibility with 0.7.67+
|
||||
|
||||
* The module version 0.1.8 should be compatibility with Nginx-1.0.14+
|
||||
|
||||
Notes
|
||||
TODO
|
||||
Known Issues
|
||||
Changelogs
|
||||
v0.3
|
||||
* support keepalive check requests
|
||||
|
||||
* fastcgi check requests
|
||||
|
||||
* json/csv check status page support
|
||||
|
||||
v0.1
|
||||
* first release
|
||||
|
||||
Authors
|
||||
Weibin Yao(姚伟斌) *yaoweibin at gmail dot com*
|
||||
|
||||
Matthieu Tourne
|
||||
|
||||
Copyright & License
|
||||
This README template copy from agentzh (<http://github.com/agentzh>).
|
||||
|
||||
The health check part is borrowed the design of Jack Lindamood's
|
||||
healthcheck module healthcheck_nginx_upstreams
|
||||
(<http://github.com/cep21/healthcheck_nginx_upstreams>);
|
||||
|
||||
This module is licensed under the BSD license.
|
||||
|
||||
Copyright (C) 2014 by Weibin Yao <yaoweibin@gmail.com>
|
||||
|
||||
Copyright (C) 2010-2014 Alibaba Group Holding Limited
|
||||
|
||||
Copyright (C) 2014 by LiangBin Li
|
||||
|
||||
Copyright (C) 2014 by Zhuo Yuan
|
||||
|
||||
Copyright (C) 2012 by Matthieu Tourne
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
@ -0,0 +1,327 @@
|
||||
= Name =
|
||||
|
||||
'''nginx_http_upstream_check_module''' - support upstream health check with Nginx
|
||||
|
||||
= Synopsis =
|
||||
|
||||
<geshi lang="nginx">
|
||||
|
||||
http {
|
||||
|
||||
upstream cluster {
|
||||
|
||||
# simple round-robin
|
||||
server 192.168.0.1:80;
|
||||
server 192.168.0.2:80;
|
||||
|
||||
check interval=5000 rise=1 fall=3 timeout=4000;
|
||||
|
||||
#check interval=3000 rise=2 fall=5 timeout=1000 type=ssl_hello;
|
||||
|
||||
#check interval=3000 rise=2 fall=5 timeout=1000 type=http;
|
||||
#check_http_send "HEAD / HTTP/1.0\r\n\r\n";
|
||||
#check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
location / {
|
||||
proxy_pass http://cluster;
|
||||
}
|
||||
|
||||
location /status {
|
||||
check_status;
|
||||
|
||||
access_log off;
|
||||
allow SOME.IP.ADD.RESS;
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</geshi>
|
||||
|
||||
= Description =
|
||||
|
||||
Add the support of health check with the upstream servers.
|
||||
|
||||
= Directives =
|
||||
|
||||
== check ==
|
||||
|
||||
'''syntax:''' ''check interval=milliseconds [fall=count] [rise=count] [timeout=milliseconds] [default_down=true|false] [type=tcp|http|ssl_hello|mysql|ajp|fastcgi]''
|
||||
|
||||
'''default:''' ''none, if parameters omitted, default parameters are interval=30000 fall=5 rise=2 timeout=1000 default_down=true type=tcp''
|
||||
|
||||
'''context:''' ''upstream''
|
||||
|
||||
'''description:''' Add the health check for the upstream servers.
|
||||
|
||||
The parameters' meanings are:
|
||||
|
||||
* ''interval'': the check request's interval time.
|
||||
* ''fall''(fall_count): After fall_count check failures, the server is marked down.
|
||||
* ''rise''(rise_count): After rise_count check success, the server is marked up.
|
||||
* ''timeout'': the check request's timeout.
|
||||
* ''default_down'': set initial state of backend server, default is down.
|
||||
* ''port'': specify the check port in the backend servers. It can be different with the original servers port. Default the port is 0 and it means the same as the original backend server.
|
||||
* ''type'': the check protocol type:
|
||||
# ''tcp'' is a simple tcp socket connect and peek one byte.
|
||||
# ''ssl_hello'' sends a client ssl hello packet and receives the server ssl hello packet.
|
||||
# ''http'' sends a http request packet, receives and parses the http response to diagnose if the upstream server is alive.
|
||||
# ''mysql'' connects to the mysql server, receives the greeting response to diagnose if the upstream server is alive.
|
||||
# ''ajp'' sends a AJP Cping packet, receives and parses the AJP Cpong response to diagnose if the upstream server is alive.
|
||||
# ''fastcgi'' send a fastcgi request, receives and parses the fastcgi response to diagnose if the upstream server is alive.
|
||||
|
||||
== check_http_send ==
|
||||
|
||||
'''syntax:''' ''check_http_send http_packet''
|
||||
|
||||
'''default:''' ''"GET / HTTP/1.0\r\n\r\n"''
|
||||
|
||||
'''context:''' ''upstream''
|
||||
|
||||
'''description:''' If you set the check type is http, then the check function will sends this http packet to check the upstream server.
|
||||
|
||||
== check_http_expect_alive ==
|
||||
|
||||
'''syntax:''' ''check_http_expect_alive [ http_2xx | http_3xx | http_4xx | http_5xx ]''
|
||||
|
||||
'''default:''' ''http_2xx | http_3xx''
|
||||
|
||||
'''context:''' ''upstream''
|
||||
|
||||
'''description:''' These status codes indicate the upstream server's http response is ok, the backend is alive.
|
||||
|
||||
== check_keepalive_requests ==
|
||||
|
||||
'''syntax:''' ''check_keepalive_requests num''
|
||||
|
||||
'''default:''' ''check_keepalive_requests 1''
|
||||
|
||||
'''context:''' ''upstream''
|
||||
|
||||
'''description:''' The directive specifies the number of requests sent on a connection, the default vaule 1 indicates that nginx will certainly close the connection after a request.
|
||||
|
||||
== check_fastcgi_param ==
|
||||
|
||||
'''Syntax:''' ''check_fastcgi_params parameter value''
|
||||
|
||||
'''default:''' see below
|
||||
|
||||
'''context:''' ''upstream''
|
||||
|
||||
'''description:''' If you set the check type is fastcgi, then the check function will sends this fastcgi headers to check the upstream server. The default directive looks like:
|
||||
|
||||
<geshi lang="nginx">
|
||||
check_fastcgi_param "REQUEST_METHOD" "GET";
|
||||
check_fastcgi_param "REQUEST_URI" "/";
|
||||
check_fastcgi_param "SCRIPT_FILENAME" "index.php";
|
||||
</geshi>
|
||||
|
||||
== check_shm_size ==
|
||||
|
||||
'''syntax:''' ''check_shm_size size''
|
||||
|
||||
'''default:''' ''1M''
|
||||
|
||||
'''context:''' ''http''
|
||||
|
||||
'''description:''' Default size is one megabytes. If you check thousands of servers, the shared memory for health check may be not enough, you can enlarge it with this directive.
|
||||
|
||||
== check_status ==
|
||||
|
||||
'''syntax:''' ''check_status [html|csv|json]''
|
||||
|
||||
'''default:''' ''none''
|
||||
|
||||
'''context:''' ''location''
|
||||
|
||||
'''description:''' Display the health checking servers' status by HTTP. This directive should be set in the http block.
|
||||
|
||||
You can specify the default display format. The formats can be `html`, `csv` or `json`. The default type is `html`. It also supports to specify the format by the request argument. Suppose your `check_status` location is '/status', the argument of `format` can change the display page's format. You can do like this:
|
||||
|
||||
<geshi lang="bash">
|
||||
/status?format=html
|
||||
/status?format=csv
|
||||
/status?format=json
|
||||
</geshi>
|
||||
|
||||
At present, you can fetch the list of servers with the same status by the argument of `status`. For example:
|
||||
|
||||
<geshi lang="bash">
|
||||
/status?format=html&status=down
|
||||
/status?format=csv&status=up
|
||||
</geshi>
|
||||
|
||||
Below it's the sample html page:
|
||||
|
||||
<geshi lang="bash">
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Nginx http upstream check status</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Nginx http upstream check status</h1>
|
||||
<h2>Check upstream server number: 1, generation: 3</h2>
|
||||
<table style="background-color:white" cellspacing="0" cellpadding="3" border="1">
|
||||
<tr bgcolor="#C0C0C0">
|
||||
<th>Index</th>
|
||||
<th>Upstream</th>
|
||||
<th>Name</th>
|
||||
<th>Status</th>
|
||||
<th>Rise counts</th>
|
||||
<th>Fall counts</th>
|
||||
<th>Check type</th>
|
||||
<th>Check port</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>backend</td>
|
||||
<td>106.187.48.116:80</td>
|
||||
<td>up</td>
|
||||
<td>39</td>
|
||||
<td>0</td>
|
||||
<td>http</td>
|
||||
<td>80</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Below it's the sample of csv page:
|
||||
|
||||
<geshi lang="bash">
|
||||
0,backend,106.187.48.116:80,up,46,0,http,80
|
||||
</geshi>
|
||||
|
||||
Below it's the sample of json page:
|
||||
|
||||
<geshi lang="bash">
|
||||
{"servers": {
|
||||
"total": 1,
|
||||
"generation": 3,
|
||||
"server": [
|
||||
{"index": 0, "upstream": "backend", "name": "106.187.48.116:80", "status": "up", "rise": 58, "fall": 0, "type": "http", "port": 80}
|
||||
]
|
||||
}}
|
||||
</geshi>
|
||||
|
||||
|
||||
= Installation =
|
||||
|
||||
Download the latest version of the release tarball of this module from [http://github.com/yaoweibin/nginx_upstream_check_module github]
|
||||
|
||||
Grab the nginx source code from [http://nginx.org/ nginx.org], for example, the version 1.0.14 (see nginx compatibility), and then build the source with this module:
|
||||
|
||||
<geshi lang="bash">
|
||||
$ wget 'http://nginx.org/download/nginx-1.0.14.tar.gz'
|
||||
$ tar -xzvf nginx-1.0.14.tar.gz
|
||||
$ cd nginx-1.0.14/
|
||||
$ patch -p1 < /path/to/nginx_http_upstream_check_module/check.patch
|
||||
|
||||
$ ./configure --add-module=/path/to/nginx_http_upstream_check_module
|
||||
|
||||
$ make
|
||||
$ make install
|
||||
</geshi>
|
||||
|
||||
= Note =
|
||||
|
||||
If you use nginx-1.2.1 or nginx-1.3.0, the nginx upstream round robin module changed greatly. You should use the patch named 'check_1.2.1.patch'.
|
||||
|
||||
If you use nginx-1.2.2+ or nginx-1.3.1+, It added the upstream least_conn module. You should use the patch named 'check_1.2.2+.patch'.
|
||||
|
||||
If you use nginx-1.2.6+ or nginx-1.3.9+, It adjusted the round robin module. You should use the patch named 'check_1.2.6+.patch'.
|
||||
|
||||
If you use nginx-1.5.12+, You should use the patch named 'check_1.5.12+.patch'.
|
||||
|
||||
If you use nginx-1.7.2+, You should use the patch named 'check_1.7.2+.patch'.
|
||||
|
||||
The patch just adds the support for the official Round-Robin, Ip_hash and least_conn upstream module. But it's easy to expand my module to other upstream modules. See the patch for detail.
|
||||
|
||||
If you want to add the support for upstream fair module, you can do it like this:
|
||||
|
||||
<geshi lang="bash">
|
||||
$ git clone git://github.com/gnosek/nginx-upstream-fair.git
|
||||
$ cd nginx-upstream-fair
|
||||
$ patch -p2 < /path/to/nginx_http_upstream_check_module/upstream_fair.patch
|
||||
$ cd /path/to/nginx-1.0.14
|
||||
$ ./configure --add-module=/path/to/nginx_http_upstream_check_module --add-module=/path/to/nginx-upstream-fair-module
|
||||
$ make
|
||||
$ make install
|
||||
</geshi>
|
||||
|
||||
If you want to add the support for nginx sticky module, you can do it like this:
|
||||
|
||||
<geshi lang="bash">
|
||||
$ svn checkout http://nginx-sticky-module.googlecode.com/svn/trunk/ nginx-sticky-module
|
||||
$ cd nginx-sticky-module
|
||||
$ patch -p0 < /path/to/nginx_http_upstream_check_module/nginx-sticky-module.patch
|
||||
$ cd /path/to/nginx-1.0.14
|
||||
$ ./configure --add-module=/path/to/nginx_http_upstream_check_module --add-module=/path/to/nginx-sticky-module
|
||||
$ make
|
||||
$ make install
|
||||
</geshi>
|
||||
|
||||
Note that, the nginx-sticky-module also needs the original check.patch.
|
||||
|
||||
|
||||
= Compatibility =
|
||||
|
||||
* The module version 0.1.5 should be compatibility with 0.7.67+
|
||||
* The module version 0.1.8 should be compatibility with Nginx-1.0.14+
|
||||
|
||||
= Notes =
|
||||
|
||||
= TODO =
|
||||
|
||||
= Known Issues =
|
||||
|
||||
= Changelogs =
|
||||
|
||||
== v0.3 ==
|
||||
* support keepalive check requests
|
||||
* fastcgi check requests
|
||||
* json/csv check status page support
|
||||
|
||||
== v0.1 ==
|
||||
* first release
|
||||
|
||||
= Authors =
|
||||
|
||||
Weibin Yao(姚伟斌) ''yaoweibin at gmail dot com''
|
||||
|
||||
Matthieu Tourne
|
||||
|
||||
|
||||
= Copyright & License =
|
||||
|
||||
This README template copy from [http://github.com/agentzh agentzh].
|
||||
|
||||
The health check part is borrowed the design of Jack Lindamood's healthcheck module [http://github.com/cep21/healthcheck_nginx_upstreams healthcheck_nginx_upstreams];
|
||||
|
||||
This module is licensed under the BSD license.
|
||||
|
||||
Copyright (C) 2014 by Weibin Yao <yaoweibin@gmail.com>
|
||||
|
||||
Copyright (C) 2010-2014 Alibaba Group Holding Limited
|
||||
|
||||
Copyright (C) 2014 by LiangBin Li
|
||||
|
||||
Copyright (C) 2014 by Zhuo Yuan
|
||||
|
||||
Copyright (C) 2012 by Matthieu Tourne
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
@ -0,0 +1,58 @@
|
||||
Index: ngx_http_sticky_module.c
|
||||
===================================================================
|
||||
--- ngx_http_sticky_module.c (revision 45)
|
||||
+++ ngx_http_sticky_module.c (working copy)
|
||||
@@ -10,6 +10,11 @@
|
||||
|
||||
#include "ngx_http_sticky_misc.h"
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
+
|
||||
+
|
||||
/* define a peer */
|
||||
typedef struct {
|
||||
ngx_http_upstream_rr_peer_t *rr_peer;
|
||||
@@ -287,6 +292,16 @@
|
||||
return NGX_BUSY;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get sticky peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ return NGX_BUSY;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
/* if it's been ignored for long enought (fail_timeout), reset timeout */
|
||||
/* do this check before testing peer->fails ! :) */
|
||||
if (now - peer->accessed > peer->fail_timeout) {
|
||||
@@ -303,6 +318,14 @@
|
||||
/* ensure the peer is not marked as down */
|
||||
if (!peer->down) {
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "get sticky peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+
|
||||
+ if (!ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+#endif
|
||||
+
|
||||
/* if it's not failedi, use it */
|
||||
if (peer->max_fails == 0 || peer->fails < peer->max_fails) {
|
||||
selected_peer = (ngx_int_t)n;
|
||||
@@ -317,6 +340,9 @@
|
||||
/* mark the peer as tried */
|
||||
iphp->rrp.tried[n] |= m;
|
||||
}
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ }
|
||||
+#endif
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,370 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
use Test::More;
|
||||
|
||||
BEGIN { use FindBin; chdir($FindBin::Bin); }
|
||||
|
||||
use lib 'lib';
|
||||
use Test::Nginx;
|
||||
|
||||
###############################################################################
|
||||
|
||||
select STDERR; $| = 1;
|
||||
select STDOUT; $| = 1;
|
||||
|
||||
eval { require FCGI; };
|
||||
plan(skip_all => 'FCGI not installed') if $@;
|
||||
plan(skip_all => 'win32') if $^O eq 'MSWin32';
|
||||
|
||||
my $t = Test::Nginx->new()->has(qw/http fastcgi/)->plan(30)
|
||||
->write_file_expand('nginx.conf', <<'EOF');
|
||||
|
||||
%%TEST_GLOBALS%%
|
||||
|
||||
daemon off;
|
||||
|
||||
events {
|
||||
}
|
||||
|
||||
http {
|
||||
%%TEST_GLOBALS_HTTP%%
|
||||
|
||||
server {
|
||||
listen 127.0.0.1:8080;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
fastcgi_pass 127.0.0.1:8081;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EOF
|
||||
|
||||
$t->run_daemon(\&fastcgi_daemon);
|
||||
$t->run();
|
||||
|
||||
###############################################################################
|
||||
|
||||
like(http_get('/'), qr/SEE-THIS/, 'fastcgi request');
|
||||
like(http_get('/redir'), qr/302/, 'fastcgi redirect');
|
||||
like(http_get('/'), qr/^3$/m, 'fastcgi third request');
|
||||
|
||||
unlike(http_head('/'), qr/SEE-THIS/, 'no data in HEAD');
|
||||
|
||||
like(http_get('/stderr'), qr/SEE-THIS/, 'large stderr handled');
|
||||
|
||||
$t->stop();
|
||||
$t->stop_daemons();
|
||||
|
||||
###############################################################################
|
||||
|
||||
$t->write_file_expand('nginx.conf', <<'EOF');
|
||||
|
||||
%%TEST_GLOBALS%%
|
||||
|
||||
daemon off;
|
||||
|
||||
worker_processes auto;
|
||||
|
||||
events {
|
||||
accept_mutex off;
|
||||
}
|
||||
|
||||
http {
|
||||
%%TEST_GLOBALS_HTTP%%
|
||||
|
||||
upstream fastcgi {
|
||||
server 127.0.0.1:8081;
|
||||
check interval=3000 rise=2 fall=3 timeout=1000 type=fastcgi default_down=false;
|
||||
check_fastcgi_param "REQUEST_METHOD" "GET";
|
||||
check_fastcgi_param "REQUEST_URI" "/redir";
|
||||
check_http_expect_alive http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 127.0.0.1:8080;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
fastcgi_pass fastcgi;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EOF
|
||||
|
||||
$t->run();
|
||||
$t->run_daemon(\&fastcgi_daemon);
|
||||
|
||||
###############################################################################
|
||||
|
||||
like(http_get('/'), qr/SEE-THIS/, 'fastcgi request default_down=false');
|
||||
like(http_get('/redir'), qr/302/, 'fastcgi redirect default_down=false');
|
||||
like(http_get('/'), qr/^3$/m, 'fastcgi third request default_down=false');
|
||||
|
||||
unlike(http_head('/'), qr/SEE-THIS/, 'no data in HEAD default_down=false');
|
||||
|
||||
like(http_get('/stderr'), qr/SEE-THIS/, 'large stderr handled default_down=false');
|
||||
|
||||
$t->stop();
|
||||
$t->stop_daemons();
|
||||
|
||||
###############################################################################
|
||||
|
||||
$t->write_file_expand('nginx.conf', <<'EOF');
|
||||
|
||||
%%TEST_GLOBALS%%
|
||||
|
||||
daemon off;
|
||||
|
||||
worker_processes auto;
|
||||
|
||||
events {
|
||||
accept_mutex off;
|
||||
}
|
||||
|
||||
http {
|
||||
%%TEST_GLOBALS_HTTP%%
|
||||
|
||||
upstream fastcgi {
|
||||
server 127.0.0.1:8081;
|
||||
check interval=3000 rise=2 fall=3 timeout=1000 type=fastcgi;
|
||||
check_fastcgi_param "REQUEST_METHOD" "GET";
|
||||
check_fastcgi_param "REQUEST_URI" "/redir";
|
||||
check_http_expect_alive http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 127.0.0.1:8080;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
fastcgi_pass fastcgi;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EOF
|
||||
|
||||
$t->run();
|
||||
$t->run_daemon(\&fastcgi_daemon);
|
||||
|
||||
###############################################################################
|
||||
|
||||
like(http_get('/'), qr/502/m, 'fastcgi request default_down=true');
|
||||
like(http_get('/redir'), qr/502/m, 'fastcgi redirect default_down=true');
|
||||
like(http_get('/'), qr/502/m, 'fastcgi third request default_down=true');
|
||||
like(http_head('/'), qr/502/m, 'no data in HEAD default_down=true');
|
||||
like(http_get('/stderr'), qr/502/m, 'large stderr handled default_down=true');
|
||||
|
||||
$t->stop();
|
||||
$t->stop_daemons();
|
||||
|
||||
###############################################################################
|
||||
|
||||
$t->write_file_expand('nginx.conf', <<'EOF');
|
||||
|
||||
%%TEST_GLOBALS%%
|
||||
|
||||
daemon off;
|
||||
|
||||
worker_processes auto;
|
||||
|
||||
events {
|
||||
accept_mutex off;
|
||||
}
|
||||
|
||||
http {
|
||||
%%TEST_GLOBALS_HTTP%%
|
||||
|
||||
upstream fastcgi {
|
||||
server 127.0.0.1:8081;
|
||||
check interval=3000 rise=2 fall=3 timeout=1000 type=fastcgi;
|
||||
check_fastcgi_param "REQUEST_METHOD" "GET";
|
||||
check_fastcgi_param "REQUEST_URI" "/redir";
|
||||
check_http_expect_alive http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 127.0.0.1:8080;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
fastcgi_pass fastcgi;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EOF
|
||||
|
||||
$t->run();
|
||||
$t->run_daemon(\&fastcgi_daemon);
|
||||
|
||||
###############################################################################
|
||||
|
||||
sleep(5);
|
||||
|
||||
like(http_get('/'), qr/SEE-THIS/, 'fastcgi request default_down=false check 302');
|
||||
like(http_get('/redir'), qr/302/, 'fastcgi redirect default_down=false check 302');
|
||||
like(http_get('/'), qr/^\d$/m, 'fastcgi third request default_down=false check 302');
|
||||
|
||||
unlike(http_head('/'), qr/SEE-THIS/, 'no data in HEAD default_down=false check 302');
|
||||
|
||||
like(http_get('/stderr'), qr/SEE-THIS/, 'large stderr handled default_down=false check 302');
|
||||
|
||||
$t->stop();
|
||||
$t->stop_daemons();
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
||||
$t->write_file_expand('nginx.conf', <<'EOF');
|
||||
|
||||
%%TEST_GLOBALS%%
|
||||
|
||||
daemon off;
|
||||
|
||||
worker_processes auto;
|
||||
|
||||
events {
|
||||
accept_mutex off;
|
||||
}
|
||||
|
||||
http {
|
||||
%%TEST_GLOBALS_HTTP%%
|
||||
|
||||
upstream fastcgi {
|
||||
server 127.0.0.1:8081;
|
||||
check interval=1000 rise=1 fall=1 timeout=1000 type=fastcgi;
|
||||
check_fastcgi_param "REQUEST_METHOD" "GET";
|
||||
check_fastcgi_param "REQUEST_URI" "/404";
|
||||
check_http_expect_alive http_2xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 127.0.0.1:8080;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
fastcgi_pass fastcgi;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EOF
|
||||
|
||||
$t->run();
|
||||
$t->run_daemon(\&fastcgi_daemon);
|
||||
|
||||
###############################################################################
|
||||
|
||||
sleep(5);
|
||||
|
||||
like(http_get('/'), qr/502/m, 'fastcgi request default_down=true check status heaer');
|
||||
like(http_get('/redir'), qr/502/m, 'fastcgi redirect default_down=true check status heaer');
|
||||
like(http_get('/'), qr/502/m, 'fastcgi third request default_down=true check status heaer');
|
||||
like(http_head('/'), qr/502/m, 'no data in HEAD default_down=true check status heaer');
|
||||
like(http_get('/stderr'), qr/502/m, 'large stderr handled default_down=true check status heaer');
|
||||
|
||||
$t->stop();
|
||||
$t->stop_daemons();
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
||||
$t->write_file_expand('nginx.conf', <<'EOF');
|
||||
|
||||
%%TEST_GLOBALS%%
|
||||
|
||||
daemon off;
|
||||
|
||||
worker_processes auto;
|
||||
|
||||
events {
|
||||
accept_mutex off;
|
||||
}
|
||||
|
||||
http {
|
||||
%%TEST_GLOBALS_HTTP%%
|
||||
|
||||
upstream fastcgi {
|
||||
server 127.0.0.1:8081;
|
||||
check interval=1000 rise=1 fall=1 timeout=1000 type=fastcgi;
|
||||
check_fastcgi_param "REQUEST_METHOD" "GET";
|
||||
check_fastcgi_param "REQUEST_URI" "/";
|
||||
check_http_expect_alive http_4xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 127.0.0.1:8080;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
fastcgi_pass fastcgi;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EOF
|
||||
|
||||
$t->run();
|
||||
$t->run_daemon(\&fastcgi_daemon);
|
||||
|
||||
###############################################################################
|
||||
|
||||
sleep(5);
|
||||
|
||||
like(http_get('/'), qr/SEE-THIS/, 'fastcgi request default_down=false without status header');
|
||||
like(http_get('/redir'), qr/302/, 'fastcgi redirect default_down=false without status header');
|
||||
like(http_get('/'), qr/^\d$/m, 'fastcgi third request default_down=false without status header');
|
||||
|
||||
unlike(http_head('/'), qr/SEE-THIS/, 'no data in HEAD default_down=false without status header');
|
||||
|
||||
like(http_get('/stderr'), qr/SEE-THIS/, 'large stderr handled default_down=false without status header');
|
||||
|
||||
$t->stop();
|
||||
$t->stop_daemons();
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
||||
sub fastcgi_daemon {
|
||||
my $socket = FCGI::OpenSocket('127.0.0.1:8081', 5);
|
||||
my $request = FCGI::Request(\*STDIN, \*STDOUT, \*STDERR, \%ENV,
|
||||
$socket);
|
||||
|
||||
my $count;
|
||||
while ( $request->Accept() >= 0 ) {
|
||||
$count++;
|
||||
|
||||
if ($ENV{REQUEST_URI} eq '/stderr') {
|
||||
warn "sample stderr text" x 512;
|
||||
}
|
||||
|
||||
if ($ENV{REQUEST_URI} eq '/404') {
|
||||
print <<EOF;
|
||||
Status: 404
|
||||
EOF
|
||||
}
|
||||
|
||||
print <<EOF;
|
||||
Location: http://127.0.0.1:8080/redirect
|
||||
Content-Type: text/html
|
||||
|
||||
SEE-THIS
|
||||
$count
|
||||
EOF
|
||||
}
|
||||
|
||||
FCGI::CloseSocket($socket);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,19 @@
|
||||
#ifndef _NGX_HTTP_UPSTREAM_CHECK_MODELE_H_INCLUDED_
|
||||
#define _NGX_HTTP_UPSTREAM_CHECK_MODELE_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
ngx_uint_t ngx_http_upstream_check_add_peer(ngx_conf_t *cf,
|
||||
ngx_http_upstream_srv_conf_t *us, ngx_addr_t *peer);
|
||||
|
||||
ngx_uint_t ngx_http_upstream_check_peer_down(ngx_uint_t index);
|
||||
|
||||
void ngx_http_upstream_check_get_peer(ngx_uint_t index);
|
||||
void ngx_http_upstream_check_free_peer(ngx_uint_t index);
|
||||
|
||||
|
||||
#endif //_NGX_HTTP_UPSTREAM_CHECK_MODELE_H_INCLUDED_
|
||||
|
@ -0,0 +1,80 @@
|
||||
diff --git a/ngx_http_upstream_jvm_route_module.c b/ngx_http_upstream_jvm_route_module.c
|
||||
index 770cfa5..e8e079b 100644
|
||||
--- a/ngx_http_upstream_jvm_route_module.c
|
||||
+++ b/ngx_http_upstream_jvm_route_module.c
|
||||
@@ -13,6 +13,10 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
+
|
||||
|
||||
#define SHM_NAME_LEN 256
|
||||
|
||||
@@ -73,6 +77,9 @@ typedef struct {
|
||||
time_t fail_timeout;
|
||||
ngx_uint_t down; /* unsigned down:1; */
|
||||
ngx_str_t srun_id;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_uint_t check_index;
|
||||
+#endif
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
ngx_ssl_session_t *ssl_session; /* local to a process */
|
||||
@@ -380,6 +387,15 @@ ngx_http_upstream_init_jvm_route_rr(ngx_conf_t *cf,
|
||||
peers->peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peers->peer[n].down = server[i].down;
|
||||
peers->peer[n].weight = server[i].down ? 0 : server[i].weight;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peers->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ peers->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
|
||||
n++;
|
||||
}
|
||||
@@ -433,6 +449,15 @@ ngx_http_upstream_init_jvm_route_rr(ngx_conf_t *cf,
|
||||
backup->peer[n].max_busy = server[i].max_busy;
|
||||
backup->peer[n].fail_timeout = server[i].fail_timeout;
|
||||
backup->peer[n].down = server[i].down;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peers->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ peers->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
|
||||
n++;
|
||||
}
|
||||
@@ -490,6 +515,9 @@ ngx_http_upstream_init_jvm_route_rr(ngx_conf_t *cf,
|
||||
peers->peer[i].max_fails = 1;
|
||||
peers->peer[i].max_busy = 0;
|
||||
peers->peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
}
|
||||
|
||||
us->peer.data = peers;
|
||||
@@ -773,6 +801,12 @@ ngx_http_upstream_jvm_route_try_peer( ngx_http_upstream_jvm_route_peer_data_t *j
|
||||
return NGX_BUSY;
|
||||
}
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+ return NGX_BUSY;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (!peer->down) {
|
||||
if (peer->max_fails == 0 || peer->shared->fails < peer->max_fails) {
|
||||
return NGX_OK;
|
@ -0,0 +1,279 @@
|
||||
NAME
|
||||
Test::Nginx - Testing modules for Nginx C module development
|
||||
|
||||
DESCRIPTION
|
||||
This distribution provides two testing modules for Nginx C module
|
||||
development:
|
||||
|
||||
* Test::Nginx::LWP
|
||||
|
||||
* Test::Nginx::Socket
|
||||
|
||||
All of them are based on Test::Base.
|
||||
|
||||
Usually, Test::Nginx::Socket is preferred because it works on a much
|
||||
lower level and not that fault tolerant like Test::Nginx::LWP.
|
||||
|
||||
Also, a lot of connection hang issues (like wrong "r->main->count" value
|
||||
in nginx 0.8.x) can only be captured by Test::Nginx::Socket because
|
||||
Perl's LWP::UserAgent client will close the connection itself which will
|
||||
conceal such issues from the testers.
|
||||
|
||||
Test::Nginx automatically starts an nginx instance (from the "PATH" env)
|
||||
rooted at t/servroot/ and the default config template makes this nginx
|
||||
instance listen on the port 1984 by default. One can specify a different
|
||||
port number by setting his port number to the "TEST_NGINX_PORT"
|
||||
environment, as in
|
||||
|
||||
export TEST_NGINX_PORT=1989
|
||||
|
||||
etcproxy integration
|
||||
The default settings in etcproxy
|
||||
(https://github.com/chaoslawful/etcproxy) makes this small TCP proxy
|
||||
split the TCP packets into bytes and introduce 1 ms latency among them.
|
||||
|
||||
There's usually various TCP chains that we can put etcproxy into, for
|
||||
example
|
||||
|
||||
Test::Nginx <=> nginx
|
||||
$ ./etcproxy 1234 1984
|
||||
|
||||
Here we tell etcproxy to listen on port 1234 and to delegate all the TCP
|
||||
traffic to the port 1984, the default port that Test::Nginx makes nginx
|
||||
listen to.
|
||||
|
||||
And then we tell Test::Nginx to test against the port 1234, where
|
||||
etcproxy listens on, rather than the port 1984 that nginx directly
|
||||
listens on:
|
||||
|
||||
$ TEST_NGINX_CLIENT_PORT=1234 prove -r t/
|
||||
|
||||
Then the TCP chain now looks like this:
|
||||
|
||||
Test::Nginx <=> etcproxy (1234) <=> nginx (1984)
|
||||
|
||||
So etcproxy can effectively emulate extreme network conditions and
|
||||
exercise "unusual" code paths in your nginx server by your tests.
|
||||
|
||||
In practice, *tons* of weird bugs can be captured by this setting. Even
|
||||
ourselves didn't expect that this simple approach is so effective.
|
||||
|
||||
nginx <=> memcached
|
||||
We first start the memcached server daemon on port 11211:
|
||||
|
||||
memcached -p 11211 -vv
|
||||
|
||||
and then we another etcproxy instance to listen on port 11984 like this
|
||||
|
||||
$ ./etcproxy 11984 11211
|
||||
|
||||
Then we tell our t/foo.t test script to connect to 11984 rather than
|
||||
11211:
|
||||
|
||||
# foo.t
|
||||
use Test::Nginx::Socket;
|
||||
repeat_each(1);
|
||||
plan tests => 2 * repeat_each() * blocks();
|
||||
$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; # make this env take a default value
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: sanity
|
||||
--- config
|
||||
location /foo {
|
||||
set $memc_cmd set;
|
||||
set $memc_key foo;
|
||||
set $memc_value bar;
|
||||
memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;
|
||||
}
|
||||
--- request
|
||||
GET /foo
|
||||
--- response_body_like: STORED
|
||||
|
||||
The Test::Nginx library will automatically expand the special macro
|
||||
$TEST_NGINX_MEMCACHED_PORT to the environment with the same name. You
|
||||
can define your own $TEST_NGINX_BLAH_BLAH_PORT macros as long as its
|
||||
prefix is "TEST_NGINX_" and all in upper case letters.
|
||||
|
||||
And now we can run your test script against the etcproxy port 11984:
|
||||
|
||||
TEST_NGINX_MEMCACHED_PORT=11984 prove t/foo.t
|
||||
|
||||
Then the TCP chains look like this:
|
||||
|
||||
Test::Nginx <=> nginx (1984) <=> etcproxy (11984) <=> memcached (11211)
|
||||
|
||||
If "TEST_NGINX_MEMCACHED_PORT" is not set, then it will take the default
|
||||
value 11211, which is what we want when there's no etcproxy configured:
|
||||
|
||||
Test::Nginx <=> nginx (1984) <=> memcached (11211)
|
||||
|
||||
This approach also works for proxied mysql and postgres traffic. Please
|
||||
see the live test suite of ngx_drizzle and ngx_postgres for more
|
||||
details.
|
||||
|
||||
Usually we set both "TEST_NGINX_CLIENT_PORT" and
|
||||
"TEST_NGINX_MEMCACHED_PORT" (and etc) at the same time, effectively
|
||||
yielding the following chain:
|
||||
|
||||
Test::Nginx <=> etcproxy (1234) <=> nginx (1984) <=> etcproxy (11984) <=> memcached (11211)
|
||||
|
||||
as long as you run two separate etcproxy instances in two separate
|
||||
terminals.
|
||||
|
||||
It's easy to verify if the traffic actually goes through your etcproxy
|
||||
server. Just check if the terminal running etcproxy emits outputs. By
|
||||
default, etcproxy always dump out the incoming and outgoing data to
|
||||
stdout/stderr.
|
||||
|
||||
valgrind integration
|
||||
Test::Nginx has integrated support for valgrind (<http://valgrind.org>)
|
||||
even though by default it does not bother running it with the tests
|
||||
because valgrind will significantly slow down the test sutie.
|
||||
|
||||
First ensure that your valgrind executable visible in your PATH env. And
|
||||
then run your test suite with the "TEST_NGINX_USE_VALGRIND" env set to
|
||||
true:
|
||||
|
||||
TEST_NGINX_USE_VALGRIND=1 prove -r t
|
||||
|
||||
If you see false alarms, you do have a chance to skip them by defining a
|
||||
./valgrind.suppress file at the root of your module source tree, as in
|
||||
|
||||
<https://github.com/chaoslawful/drizzle-nginx-module/blob/master/valgrin
|
||||
d.suppress>
|
||||
|
||||
This is the suppression file for ngx_drizzle. Test::Nginx will
|
||||
automatically use it to start nginx with valgrind memcheck if this file
|
||||
does exist at the expected location.
|
||||
|
||||
If you do see a lot of "Connection refused" errors while running the
|
||||
tests this way, then you probably have a slow machine (or a very busy
|
||||
one) that the default waiting time is not sufficient for valgrind to
|
||||
start. You can define the sleep time to a larger value by setting the
|
||||
"TEST_NGINX_SLEEP" env:
|
||||
|
||||
TEST_NGINX_SLEEP=1 prove -r t
|
||||
|
||||
The time unit used here is "second". The default sleep setting just fits
|
||||
my ThinkPad ("Core2Duo T9600").
|
||||
|
||||
Applying the no-pool patch to your nginx core is recommended while
|
||||
running nginx with valgrind:
|
||||
|
||||
<https://github.com/shrimp/no-pool-nginx>
|
||||
|
||||
The nginx memory pool can prevent valgrind from spotting lots of invalid
|
||||
memory reads/writes as well as certain double-free errors. We did find a
|
||||
lot more memory issues in many of our modules when we first introduced
|
||||
the no-pool patch in practice ;)
|
||||
|
||||
There's also more advanced features in Test::Nginx that have never
|
||||
documented. I'd like to write more about them in the near future ;)
|
||||
|
||||
Nginx C modules that use Test::Nginx to drive their test suites
|
||||
ngx_echo
|
||||
<http://github.com/agentzh/echo-nginx-module>
|
||||
|
||||
ngx_headers_more
|
||||
<http://github.com/agentzh/headers-more-nginx-module>
|
||||
|
||||
ngx_chunkin
|
||||
<http://wiki.nginx.org/NginxHttpChunkinModule>
|
||||
|
||||
ngx_memc
|
||||
<http://wiki.nginx.org/NginxHttpMemcModule>
|
||||
|
||||
ngx_drizzle
|
||||
<http://github.com/chaoslawful/drizzle-nginx-module>
|
||||
|
||||
ngx_rds_json
|
||||
<http://github.com/agentzh/rds-json-nginx-module>
|
||||
|
||||
ngx_rds_csv
|
||||
<http://github.com/agentzh/rds-csv-nginx-module>
|
||||
|
||||
ngx_xss
|
||||
<http://github.com/agentzh/xss-nginx-module>
|
||||
|
||||
ngx_srcache
|
||||
<http://github.com/agentzh/srcache-nginx-module>
|
||||
|
||||
ngx_lua
|
||||
<http://github.com/chaoslawful/lua-nginx-module>
|
||||
|
||||
ngx_set_misc
|
||||
<http://github.com/agentzh/set-misc-nginx-module>
|
||||
|
||||
ngx_array_var
|
||||
<http://github.com/agentzh/array-var-nginx-module>
|
||||
|
||||
ngx_form_input
|
||||
<http://github.com/calio/form-input-nginx-module>
|
||||
|
||||
ngx_iconv
|
||||
<http://github.com/calio/iconv-nginx-module>
|
||||
|
||||
ngx_set_cconv
|
||||
<http://github.com/liseen/set-cconv-nginx-module>
|
||||
|
||||
ngx_postgres
|
||||
<http://github.com/FRiCKLE/ngx_postgres>
|
||||
|
||||
ngx_coolkit
|
||||
<http://github.com/FRiCKLE/ngx_coolkit>
|
||||
|
||||
Naxsi
|
||||
<http://code.google.com/p/naxsi/>
|
||||
|
||||
SOURCE REPOSITORY
|
||||
This module has a Git repository on Github, which has access for all.
|
||||
|
||||
http://github.com/agentzh/test-nginx
|
||||
|
||||
If you want a commit bit, feel free to drop me a line.
|
||||
|
||||
AUTHORS
|
||||
agentzh (章亦春) "<agentzh@gmail.com>"
|
||||
|
||||
Antoine BONAVITA "<antoine.bonavita@gmail.com>"
|
||||
|
||||
COPYRIGHT & LICENSE
|
||||
Copyright (c) 2009-2012, agentzh "<agentzh@gmail.com>".
|
||||
|
||||
Copyright (c) 2011-2012, Antoine Bonavita
|
||||
"<antoine.bonavita@gmail.com>".
|
||||
|
||||
This module is licensed under the terms of the BSD license.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the authors nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
SEE ALSO
|
||||
Test::Nginx::LWP, Test::Nginx::Socket, Test::Base.
|
||||
|
@ -0,0 +1,915 @@
|
||||
#line 1
|
||||
package Module::AutoInstall;
|
||||
|
||||
use strict;
|
||||
use Cwd ();
|
||||
use ExtUtils::MakeMaker ();
|
||||
|
||||
use vars qw{$VERSION};
|
||||
BEGIN {
|
||||
$VERSION = '1.04';
|
||||
}
|
||||
|
||||
# special map on pre-defined feature sets
|
||||
my %FeatureMap = (
|
||||
'' => 'Core Features', # XXX: deprecated
|
||||
'-core' => 'Core Features',
|
||||
);
|
||||
|
||||
# various lexical flags
|
||||
my ( @Missing, @Existing, %DisabledTests, $UnderCPAN, $InstallDepsTarget, $HasCPANPLUS );
|
||||
my (
|
||||
$Config, $CheckOnly, $SkipInstall, $AcceptDefault, $TestOnly, $AllDeps,
|
||||
$UpgradeDeps
|
||||
);
|
||||
my ( $PostambleActions, $PostambleActionsNoTest, $PostambleActionsUpgradeDeps,
|
||||
$PostambleActionsUpgradeDepsNoTest, $PostambleActionsListDeps,
|
||||
$PostambleActionsListAllDeps, $PostambleUsed, $NoTest);
|
||||
|
||||
# See if it's a testing or non-interactive session
|
||||
_accept_default( $ENV{AUTOMATED_TESTING} or ! -t STDIN );
|
||||
_init();
|
||||
|
||||
sub _accept_default {
|
||||
$AcceptDefault = shift;
|
||||
}
|
||||
|
||||
sub _installdeps_target {
|
||||
$InstallDepsTarget = shift;
|
||||
}
|
||||
|
||||
sub missing_modules {
|
||||
return @Missing;
|
||||
}
|
||||
|
||||
sub do_install {
|
||||
__PACKAGE__->install(
|
||||
[
|
||||
$Config
|
||||
? ( UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} )
|
||||
: ()
|
||||
],
|
||||
@Missing,
|
||||
);
|
||||
}
|
||||
|
||||
# initialize various flags, and/or perform install
|
||||
sub _init {
|
||||
foreach my $arg (
|
||||
@ARGV,
|
||||
split(
|
||||
/[\s\t]+/,
|
||||
$ENV{PERL_AUTOINSTALL} || $ENV{PERL_EXTUTILS_AUTOINSTALL} || ''
|
||||
)
|
||||
)
|
||||
{
|
||||
if ( $arg =~ /^--config=(.*)$/ ) {
|
||||
$Config = [ split( ',', $1 ) ];
|
||||
}
|
||||
elsif ( $arg =~ /^--installdeps=(.*)$/ ) {
|
||||
__PACKAGE__->install( $Config, @Missing = split( /,/, $1 ) );
|
||||
exit 0;
|
||||
}
|
||||
elsif ( $arg =~ /^--upgradedeps=(.*)$/ ) {
|
||||
$UpgradeDeps = 1;
|
||||
__PACKAGE__->install( $Config, @Missing = split( /,/, $1 ) );
|
||||
exit 0;
|
||||
}
|
||||
elsif ( $arg =~ /^--default(?:deps)?$/ ) {
|
||||
$AcceptDefault = 1;
|
||||
}
|
||||
elsif ( $arg =~ /^--check(?:deps)?$/ ) {
|
||||
$CheckOnly = 1;
|
||||
}
|
||||
elsif ( $arg =~ /^--skip(?:deps)?$/ ) {
|
||||
$SkipInstall = 1;
|
||||
}
|
||||
elsif ( $arg =~ /^--test(?:only)?$/ ) {
|
||||
$TestOnly = 1;
|
||||
}
|
||||
elsif ( $arg =~ /^--all(?:deps)?$/ ) {
|
||||
$AllDeps = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# overrides MakeMaker's prompt() to automatically accept the default choice
|
||||
sub _prompt {
|
||||
goto &ExtUtils::MakeMaker::prompt unless $AcceptDefault;
|
||||
|
||||
my ( $prompt, $default ) = @_;
|
||||
my $y = ( $default =~ /^[Yy]/ );
|
||||
|
||||
print $prompt, ' [', ( $y ? 'Y' : 'y' ), '/', ( $y ? 'n' : 'N' ), '] ';
|
||||
print "$default\n";
|
||||
return $default;
|
||||
}
|
||||
|
||||
# the workhorse
|
||||
sub import {
|
||||
my $class = shift;
|
||||
my @args = @_ or return;
|
||||
my $core_all;
|
||||
|
||||
print "*** $class version " . $class->VERSION . "\n";
|
||||
print "*** Checking for Perl dependencies...\n";
|
||||
|
||||
my $cwd = Cwd::cwd();
|
||||
|
||||
$Config = [];
|
||||
|
||||
my $maxlen = length(
|
||||
(
|
||||
sort { length($b) <=> length($a) }
|
||||
grep { /^[^\-]/ }
|
||||
map {
|
||||
ref($_)
|
||||
? ( ( ref($_) eq 'HASH' ) ? keys(%$_) : @{$_} )
|
||||
: ''
|
||||
}
|
||||
map { +{@args}->{$_} }
|
||||
grep { /^[^\-]/ or /^-core$/i } keys %{ +{@args} }
|
||||
)[0]
|
||||
);
|
||||
|
||||
# We want to know if we're under CPAN early to avoid prompting, but
|
||||
# if we aren't going to try and install anything anyway then skip the
|
||||
# check entirely since we don't want to have to load (and configure)
|
||||
# an old CPAN just for a cosmetic message
|
||||
|
||||
$UnderCPAN = _check_lock(1) unless $SkipInstall || $InstallDepsTarget;
|
||||
|
||||
while ( my ( $feature, $modules ) = splice( @args, 0, 2 ) ) {
|
||||
my ( @required, @tests, @skiptests );
|
||||
my $default = 1;
|
||||
my $conflict = 0;
|
||||
|
||||
if ( $feature =~ m/^-(\w+)$/ ) {
|
||||
my $option = lc($1);
|
||||
|
||||
# check for a newer version of myself
|
||||
_update_to( $modules, @_ ) and return if $option eq 'version';
|
||||
|
||||
# sets CPAN configuration options
|
||||
$Config = $modules if $option eq 'config';
|
||||
|
||||
# promote every features to core status
|
||||
$core_all = ( $modules =~ /^all$/i ) and next
|
||||
if $option eq 'core';
|
||||
|
||||
next unless $option eq 'core';
|
||||
}
|
||||
|
||||
print "[" . ( $FeatureMap{ lc($feature) } || $feature ) . "]\n";
|
||||
|
||||
$modules = [ %{$modules} ] if UNIVERSAL::isa( $modules, 'HASH' );
|
||||
|
||||
unshift @$modules, -default => &{ shift(@$modules) }
|
||||
if ( ref( $modules->[0] ) eq 'CODE' ); # XXX: bugward combatability
|
||||
|
||||
while ( my ( $mod, $arg ) = splice( @$modules, 0, 2 ) ) {
|
||||
if ( $mod =~ m/^-(\w+)$/ ) {
|
||||
my $option = lc($1);
|
||||
|
||||
$default = $arg if ( $option eq 'default' );
|
||||
$conflict = $arg if ( $option eq 'conflict' );
|
||||
@tests = @{$arg} if ( $option eq 'tests' );
|
||||
@skiptests = @{$arg} if ( $option eq 'skiptests' );
|
||||
|
||||
next;
|
||||
}
|
||||
|
||||
printf( "- %-${maxlen}s ...", $mod );
|
||||
|
||||
if ( $arg and $arg =~ /^\D/ ) {
|
||||
unshift @$modules, $arg;
|
||||
$arg = 0;
|
||||
}
|
||||
|
||||
# XXX: check for conflicts and uninstalls(!) them.
|
||||
my $cur = _load($mod);
|
||||
if (_version_cmp ($cur, $arg) >= 0)
|
||||
{
|
||||
print "loaded. ($cur" . ( $arg ? " >= $arg" : '' ) . ")\n";
|
||||
push @Existing, $mod => $arg;
|
||||
$DisabledTests{$_} = 1 for map { glob($_) } @skiptests;
|
||||
}
|
||||
else {
|
||||
if (not defined $cur) # indeed missing
|
||||
{
|
||||
print "missing." . ( $arg ? " (would need $arg)" : '' ) . "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
# no need to check $arg as _version_cmp ($cur, undef) would satisfy >= above
|
||||
print "too old. ($cur < $arg)\n";
|
||||
}
|
||||
|
||||
push @required, $mod => $arg;
|
||||
}
|
||||
}
|
||||
|
||||
next unless @required;
|
||||
|
||||
my $mandatory = ( $feature eq '-core' or $core_all );
|
||||
|
||||
if (
|
||||
!$SkipInstall
|
||||
and (
|
||||
$CheckOnly
|
||||
or ($mandatory and $UnderCPAN)
|
||||
or $AllDeps
|
||||
or $InstallDepsTarget
|
||||
or _prompt(
|
||||
qq{==> Auto-install the }
|
||||
. ( @required / 2 )
|
||||
. ( $mandatory ? ' mandatory' : ' optional' )
|
||||
. qq{ module(s) from CPAN?},
|
||||
$default ? 'y' : 'n',
|
||||
) =~ /^[Yy]/
|
||||
)
|
||||
)
|
||||
{
|
||||
push( @Missing, @required );
|
||||
$DisabledTests{$_} = 1 for map { glob($_) } @skiptests;
|
||||
}
|
||||
|
||||
elsif ( !$SkipInstall
|
||||
and $default
|
||||
and $mandatory
|
||||
and
|
||||
_prompt( qq{==> The module(s) are mandatory! Really skip?}, 'n', )
|
||||
=~ /^[Nn]/ )
|
||||
{
|
||||
push( @Missing, @required );
|
||||
$DisabledTests{$_} = 1 for map { glob($_) } @skiptests;
|
||||
}
|
||||
|
||||
else {
|
||||
$DisabledTests{$_} = 1 for map { glob($_) } @tests;
|
||||
}
|
||||
}
|
||||
|
||||
if ( @Missing and not( $CheckOnly or $UnderCPAN) ) {
|
||||
require Config;
|
||||
my $make = $Config::Config{make};
|
||||
if ($InstallDepsTarget) {
|
||||
print
|
||||
"*** To install dependencies type '$make installdeps' or '$make installdeps_notest'.\n";
|
||||
}
|
||||
else {
|
||||
print
|
||||
"*** Dependencies will be installed the next time you type '$make'.\n";
|
||||
}
|
||||
|
||||
# make an educated guess of whether we'll need root permission.
|
||||
print " (You may need to do that as the 'root' user.)\n"
|
||||
if eval '$>';
|
||||
}
|
||||
print "*** $class configuration finished.\n";
|
||||
|
||||
chdir $cwd;
|
||||
|
||||
# import to main::
|
||||
no strict 'refs';
|
||||
*{'main::WriteMakefile'} = \&Write if caller(0) eq 'main';
|
||||
|
||||
return (@Existing, @Missing);
|
||||
}
|
||||
|
||||
sub _running_under {
|
||||
my $thing = shift;
|
||||
print <<"END_MESSAGE";
|
||||
*** Since we're running under ${thing}, I'll just let it take care
|
||||
of the dependency's installation later.
|
||||
END_MESSAGE
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Check to see if we are currently running under CPAN.pm and/or CPANPLUS;
|
||||
# if we are, then we simply let it taking care of our dependencies
|
||||
sub _check_lock {
|
||||
return unless @Missing or @_;
|
||||
|
||||
if ($ENV{PERL5_CPANM_IS_RUNNING}) {
|
||||
return _running_under('cpanminus');
|
||||
}
|
||||
|
||||
my $cpan_env = $ENV{PERL5_CPAN_IS_RUNNING};
|
||||
|
||||
if ($ENV{PERL5_CPANPLUS_IS_RUNNING}) {
|
||||
return _running_under($cpan_env ? 'CPAN' : 'CPANPLUS');
|
||||
}
|
||||
|
||||
require CPAN;
|
||||
|
||||
if ($CPAN::VERSION > '1.89') {
|
||||
if ($cpan_env) {
|
||||
return _running_under('CPAN');
|
||||
}
|
||||
return; # CPAN.pm new enough, don't need to check further
|
||||
}
|
||||
|
||||
# last ditch attempt, this -will- configure CPAN, very sorry
|
||||
|
||||
_load_cpan(1); # force initialize even though it's already loaded
|
||||
|
||||
# Find the CPAN lock-file
|
||||
my $lock = MM->catfile( $CPAN::Config->{cpan_home}, ".lock" );
|
||||
return unless -f $lock;
|
||||
|
||||
# Check the lock
|
||||
local *LOCK;
|
||||
return unless open(LOCK, $lock);
|
||||
|
||||
if (
|
||||
( $^O eq 'MSWin32' ? _under_cpan() : <LOCK> == getppid() )
|
||||
and ( $CPAN::Config->{prerequisites_policy} || '' ) ne 'ignore'
|
||||
) {
|
||||
print <<'END_MESSAGE';
|
||||
|
||||
*** Since we're running under CPAN, I'll just let it take care
|
||||
of the dependency's installation later.
|
||||
END_MESSAGE
|
||||
return 1;
|
||||
}
|
||||
|
||||
close LOCK;
|
||||
return;
|
||||
}
|
||||
|
||||
sub install {
|
||||
my $class = shift;
|
||||
|
||||
my $i; # used below to strip leading '-' from config keys
|
||||
my @config = ( map { s/^-// if ++$i; $_ } @{ +shift } );
|
||||
|
||||
my ( @modules, @installed );
|
||||
while ( my ( $pkg, $ver ) = splice( @_, 0, 2 ) ) {
|
||||
|
||||
# grep out those already installed
|
||||
if ( _version_cmp( _load($pkg), $ver ) >= 0 ) {
|
||||
push @installed, $pkg;
|
||||
}
|
||||
else {
|
||||
push @modules, $pkg, $ver;
|
||||
}
|
||||
}
|
||||
|
||||
if ($UpgradeDeps) {
|
||||
push @modules, @installed;
|
||||
@installed = ();
|
||||
}
|
||||
|
||||
return @installed unless @modules; # nothing to do
|
||||
return @installed if _check_lock(); # defer to the CPAN shell
|
||||
|
||||
print "*** Installing dependencies...\n";
|
||||
|
||||
return unless _connected_to('cpan.org');
|
||||
|
||||
my %args = @config;
|
||||
my %failed;
|
||||
local *FAILED;
|
||||
if ( $args{do_once} and open( FAILED, '.#autoinstall.failed' ) ) {
|
||||
while (<FAILED>) { chomp; $failed{$_}++ }
|
||||
close FAILED;
|
||||
|
||||
my @newmod;
|
||||
while ( my ( $k, $v ) = splice( @modules, 0, 2 ) ) {
|
||||
push @newmod, ( $k => $v ) unless $failed{$k};
|
||||
}
|
||||
@modules = @newmod;
|
||||
}
|
||||
|
||||
if ( _has_cpanplus() and not $ENV{PERL_AUTOINSTALL_PREFER_CPAN} ) {
|
||||
_install_cpanplus( \@modules, \@config );
|
||||
} else {
|
||||
_install_cpan( \@modules, \@config );
|
||||
}
|
||||
|
||||
print "*** $class installation finished.\n";
|
||||
|
||||
# see if we have successfully installed them
|
||||
while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) {
|
||||
if ( _version_cmp( _load($pkg), $ver ) >= 0 ) {
|
||||
push @installed, $pkg;
|
||||
}
|
||||
elsif ( $args{do_once} and open( FAILED, '>> .#autoinstall.failed' ) ) {
|
||||
print FAILED "$pkg\n";
|
||||
}
|
||||
}
|
||||
|
||||
close FAILED if $args{do_once};
|
||||
|
||||
return @installed;
|
||||
}
|
||||
|
||||
sub _install_cpanplus {
|
||||
my @modules = @{ +shift };
|
||||
my @config = _cpanplus_config( @{ +shift } );
|
||||
my $installed = 0;
|
||||
|
||||
require CPANPLUS::Backend;
|
||||
my $cp = CPANPLUS::Backend->new;
|
||||
my $conf = $cp->configure_object;
|
||||
|
||||
return unless $conf->can('conf') # 0.05x+ with "sudo" support
|
||||
or _can_write($conf->_get_build('base')); # 0.04x
|
||||
|
||||
# if we're root, set UNINST=1 to avoid trouble unless user asked for it.
|
||||
my $makeflags = $conf->get_conf('makeflags') || '';
|
||||
if ( UNIVERSAL::isa( $makeflags, 'HASH' ) ) {
|
||||
# 0.03+ uses a hashref here
|
||||
$makeflags->{UNINST} = 1 unless exists $makeflags->{UNINST};
|
||||
|
||||
} else {
|
||||
# 0.02 and below uses a scalar
|
||||
$makeflags = join( ' ', split( ' ', $makeflags ), 'UNINST=1' )
|
||||
if ( $makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' } );
|
||||
|
||||
}
|
||||
$conf->set_conf( makeflags => $makeflags );
|
||||
$conf->set_conf( prereqs => 1 );
|
||||
|
||||
|
||||
|
||||
while ( my ( $key, $val ) = splice( @config, 0, 2 ) ) {
|
||||
$conf->set_conf( $key, $val );
|
||||
}
|
||||
|
||||
my $modtree = $cp->module_tree;
|
||||
while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) {
|
||||
print "*** Installing $pkg...\n";
|
||||
|
||||
MY::preinstall( $pkg, $ver ) or next if defined &MY::preinstall;
|
||||
|
||||
my $success;
|
||||
my $obj = $modtree->{$pkg};
|
||||
|
||||
if ( $obj and _version_cmp( $obj->{version}, $ver ) >= 0 ) {
|
||||
my $pathname = $pkg;
|
||||
$pathname =~ s/::/\\W/;
|
||||
|
||||
foreach my $inc ( grep { m/$pathname.pm/i } keys(%INC) ) {
|
||||
delete $INC{$inc};
|
||||
}
|
||||
|
||||
my $rv = $cp->install( modules => [ $obj->{module} ] );
|
||||
|
||||
if ( $rv and ( $rv->{ $obj->{module} } or $rv->{ok} ) ) {
|
||||
print "*** $pkg successfully installed.\n";
|
||||
$success = 1;
|
||||
} else {
|
||||
print "*** $pkg installation cancelled.\n";
|
||||
$success = 0;
|
||||
}
|
||||
|
||||
$installed += $success;
|
||||
} else {
|
||||
print << ".";
|
||||
*** Could not find a version $ver or above for $pkg; skipping.
|
||||
.
|
||||
}
|
||||
|
||||
MY::postinstall( $pkg, $ver, $success ) if defined &MY::postinstall;
|
||||
}
|
||||
|
||||
return $installed;
|
||||
}
|
||||
|
||||
sub _cpanplus_config {
|
||||
my @config = ();
|
||||
while ( @_ ) {
|
||||
my ($key, $value) = (shift(), shift());
|
||||
if ( $key eq 'prerequisites_policy' ) {
|
||||
if ( $value eq 'follow' ) {
|
||||
$value = CPANPLUS::Internals::Constants::PREREQ_INSTALL();
|
||||
} elsif ( $value eq 'ask' ) {
|
||||
$value = CPANPLUS::Internals::Constants::PREREQ_ASK();
|
||||
} elsif ( $value eq 'ignore' ) {
|
||||
$value = CPANPLUS::Internals::Constants::PREREQ_IGNORE();
|
||||
} else {
|
||||
die "*** Cannot convert option $key = '$value' to CPANPLUS version.\n";
|
||||
}
|
||||
push @config, 'prereqs', $value;
|
||||
} elsif ( $key eq 'force' ) {
|
||||
push @config, $key, $value;
|
||||
} elsif ( $key eq 'notest' ) {
|
||||
push @config, 'skiptest', $value;
|
||||
} else {
|
||||
die "*** Cannot convert option $key to CPANPLUS version.\n";
|
||||
}
|
||||
}
|
||||
return @config;
|
||||
}
|
||||
|
||||
sub _install_cpan {
|
||||
my @modules = @{ +shift };
|
||||
my @config = @{ +shift };
|
||||
my $installed = 0;
|
||||
my %args;
|
||||
|
||||
_load_cpan();
|
||||
require Config;
|
||||
|
||||
if (CPAN->VERSION < 1.80) {
|
||||
# no "sudo" support, probe for writableness
|
||||
return unless _can_write( MM->catfile( $CPAN::Config->{cpan_home}, 'sources' ) )
|
||||
and _can_write( $Config::Config{sitelib} );
|
||||
}
|
||||
|
||||
# if we're root, set UNINST=1 to avoid trouble unless user asked for it.
|
||||
my $makeflags = $CPAN::Config->{make_install_arg} || '';
|
||||
$CPAN::Config->{make_install_arg} =
|
||||
join( ' ', split( ' ', $makeflags ), 'UNINST=1' )
|
||||
if ( $makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' } );
|
||||
|
||||
# don't show start-up info
|
||||
$CPAN::Config->{inhibit_startup_message} = 1;
|
||||
|
||||
# set additional options
|
||||
while ( my ( $opt, $arg ) = splice( @config, 0, 2 ) ) {
|
||||
( $args{$opt} = $arg, next )
|
||||
if $opt =~ /^(?:force|notest)$/; # pseudo-option
|
||||
$CPAN::Config->{$opt} = $arg;
|
||||
}
|
||||
|
||||
if ($args{notest} && (not CPAN::Shell->can('notest'))) {
|
||||
die "Your version of CPAN is too old to support the 'notest' pragma";
|
||||
}
|
||||
|
||||
local $CPAN::Config->{prerequisites_policy} = 'follow';
|
||||
|
||||
while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) {
|
||||
MY::preinstall( $pkg, $ver ) or next if defined &MY::preinstall;
|
||||
|
||||
print "*** Installing $pkg...\n";
|
||||
|
||||
my $obj = CPAN::Shell->expand( Module => $pkg );
|
||||
my $success = 0;
|
||||
|
||||
if ( $obj and _version_cmp( $obj->cpan_version, $ver ) >= 0 ) {
|
||||
my $pathname = $pkg;
|
||||
$pathname =~ s/::/\\W/;
|
||||
|
||||
foreach my $inc ( grep { m/$pathname.pm/i } keys(%INC) ) {
|
||||
delete $INC{$inc};
|
||||
}
|
||||
|
||||
my $rv = do {
|
||||
if ($args{force}) {
|
||||
CPAN::Shell->force( install => $pkg )
|
||||
} elsif ($args{notest}) {
|
||||
CPAN::Shell->notest( install => $pkg )
|
||||
} else {
|
||||
CPAN::Shell->install($pkg)
|
||||
}
|
||||
};
|
||||
|
||||
$rv ||= eval {
|
||||
$CPAN::META->instance( 'CPAN::Distribution', $obj->cpan_file, )
|
||||
->{install}
|
||||
if $CPAN::META;
|
||||
};
|
||||
|
||||
if ( $rv eq 'YES' ) {
|
||||
print "*** $pkg successfully installed.\n";
|
||||
$success = 1;
|
||||
}
|
||||
else {
|
||||
print "*** $pkg installation failed.\n";
|
||||
$success = 0;
|
||||
}
|
||||
|
||||
$installed += $success;
|
||||
}
|
||||
else {
|
||||
print << ".";
|
||||
*** Could not find a version $ver or above for $pkg; skipping.
|
||||
.
|
||||
}
|
||||
|
||||
MY::postinstall( $pkg, $ver, $success ) if defined &MY::postinstall;
|
||||
}
|
||||
|
||||
return $installed;
|
||||
}
|
||||
|
||||
sub _has_cpanplus {
|
||||
return (
|
||||
$HasCPANPLUS = (
|
||||
$INC{'CPANPLUS/Config.pm'}
|
||||
or _load('CPANPLUS::Shell::Default')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
# make guesses on whether we're under the CPAN installation directory
|
||||
sub _under_cpan {
|
||||
require Cwd;
|
||||
require File::Spec;
|
||||
|
||||
my $cwd = File::Spec->canonpath( Cwd::cwd() );
|
||||
my $cpan = File::Spec->canonpath( $CPAN::Config->{cpan_home} );
|
||||
|
||||
return ( index( $cwd, $cpan ) > -1 );
|
||||
}
|
||||
|
||||
sub _update_to {
|
||||
my $class = __PACKAGE__;
|
||||
my $ver = shift;
|
||||
|
||||
return
|
||||
if _version_cmp( _load($class), $ver ) >= 0; # no need to upgrade
|
||||
|
||||
if (
|
||||
_prompt( "==> A newer version of $class ($ver) is required. Install?",
|
||||
'y' ) =~ /^[Nn]/
|
||||
)
|
||||
{
|
||||
die "*** Please install $class $ver manually.\n";
|
||||
}
|
||||
|
||||
print << ".";
|
||||
*** Trying to fetch it from CPAN...
|
||||
.
|
||||
|
||||
# install ourselves
|
||||
_load($class) and return $class->import(@_)
|
||||
if $class->install( [], $class, $ver );
|
||||
|
||||
print << '.'; exit 1;
|
||||
|
||||
*** Cannot bootstrap myself. :-( Installation terminated.
|
||||
.
|
||||
}
|
||||
|
||||
# check if we're connected to some host, using inet_aton
|
||||
sub _connected_to {
|
||||
my $site = shift;
|
||||
|
||||
return (
|
||||
( _load('Socket') and Socket::inet_aton($site) ) or _prompt(
|
||||
qq(
|
||||
*** Your host cannot resolve the domain name '$site', which
|
||||
probably means the Internet connections are unavailable.
|
||||
==> Should we try to install the required module(s) anyway?), 'n'
|
||||
) =~ /^[Yy]/
|
||||
);
|
||||
}
|
||||
|
||||
# check if a directory is writable; may create it on demand
|
||||
sub _can_write {
|
||||
my $path = shift;
|
||||
mkdir( $path, 0755 ) unless -e $path;
|
||||
|
||||
return 1 if -w $path;
|
||||
|
||||
print << ".";
|
||||
*** You are not allowed to write to the directory '$path';
|
||||
the installation may fail due to insufficient permissions.
|
||||
.
|
||||
|
||||
if (
|
||||
eval '$>' and lc(`sudo -V`) =~ /version/ and _prompt(
|
||||
qq(
|
||||
==> Should we try to re-execute the autoinstall process with 'sudo'?),
|
||||
((-t STDIN) ? 'y' : 'n')
|
||||
) =~ /^[Yy]/
|
||||
)
|
||||
{
|
||||
|
||||
# try to bootstrap ourselves from sudo
|
||||
print << ".";
|
||||
*** Trying to re-execute the autoinstall process with 'sudo'...
|
||||
.
|
||||
my $missing = join( ',', @Missing );
|
||||
my $config = join( ',',
|
||||
UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} )
|
||||
if $Config;
|
||||
|
||||
return
|
||||
unless system( 'sudo', $^X, $0, "--config=$config",
|
||||
"--installdeps=$missing" );
|
||||
|
||||
print << ".";
|
||||
*** The 'sudo' command exited with error! Resuming...
|
||||
.
|
||||
}
|
||||
|
||||
return _prompt(
|
||||
qq(
|
||||
==> Should we try to install the required module(s) anyway?), 'n'
|
||||
) =~ /^[Yy]/;
|
||||
}
|
||||
|
||||
# load a module and return the version it reports
|
||||
sub _load {
|
||||
my $mod = pop; # class/instance doesn't matter
|
||||
my $file = $mod;
|
||||
|
||||
$file =~ s|::|/|g;
|
||||
$file .= '.pm';
|
||||
|
||||
local $@;
|
||||
return eval { require $file; $mod->VERSION } || ( $@ ? undef: 0 );
|
||||
}
|
||||
|
||||
# Load CPAN.pm and it's configuration
|
||||
sub _load_cpan {
|
||||
return if $CPAN::VERSION and $CPAN::Config and not @_;
|
||||
require CPAN;
|
||||
|
||||
# CPAN-1.82+ adds CPAN::Config::AUTOLOAD to redirect to
|
||||
# CPAN::HandleConfig->load. CPAN reports that the redirection
|
||||
# is deprecated in a warning printed at the user.
|
||||
|
||||
# CPAN-1.81 expects CPAN::HandleConfig->load, does not have
|
||||
# $CPAN::HandleConfig::VERSION but cannot handle
|
||||
# CPAN::Config->load
|
||||
|
||||
# Which "versions expect CPAN::Config->load?
|
||||
|
||||
if ( $CPAN::HandleConfig::VERSION
|
||||
|| CPAN::HandleConfig->can('load')
|
||||
) {
|
||||
# Newer versions of CPAN have a HandleConfig module
|
||||
CPAN::HandleConfig->load;
|
||||
} else {
|
||||
# Older versions had the load method in Config directly
|
||||
CPAN::Config->load;
|
||||
}
|
||||
}
|
||||
|
||||
# compare two versions, either use Sort::Versions or plain comparison
|
||||
# return values same as <=>
|
||||
sub _version_cmp {
|
||||
my ( $cur, $min ) = @_;
|
||||
return -1 unless defined $cur; # if 0 keep comparing
|
||||
return 1 unless $min;
|
||||
|
||||
$cur =~ s/\s+$//;
|
||||
|
||||
# check for version numbers that are not in decimal format
|
||||
if ( ref($cur) or ref($min) or $cur =~ /v|\..*\./ or $min =~ /v|\..*\./ ) {
|
||||
if ( ( $version::VERSION or defined( _load('version') )) and
|
||||
version->can('new')
|
||||
) {
|
||||
|
||||
# use version.pm if it is installed.
|
||||
return version->new($cur) <=> version->new($min);
|
||||
}
|
||||
elsif ( $Sort::Versions::VERSION or defined( _load('Sort::Versions') ) )
|
||||
{
|
||||
|
||||
# use Sort::Versions as the sorting algorithm for a.b.c versions
|
||||
return Sort::Versions::versioncmp( $cur, $min );
|
||||
}
|
||||
|
||||
warn "Cannot reliably compare non-decimal formatted versions.\n"
|
||||
. "Please install version.pm or Sort::Versions.\n";
|
||||
}
|
||||
|
||||
# plain comparison
|
||||
local $^W = 0; # shuts off 'not numeric' bugs
|
||||
return $cur <=> $min;
|
||||
}
|
||||
|
||||
# nothing; this usage is deprecated.
|
||||
sub main::PREREQ_PM { return {}; }
|
||||
|
||||
sub _make_args {
|
||||
my %args = @_;
|
||||
|
||||
$args{PREREQ_PM} = { %{ $args{PREREQ_PM} || {} }, @Existing, @Missing }
|
||||
if $UnderCPAN or $TestOnly;
|
||||
|
||||
if ( $args{EXE_FILES} and -e 'MANIFEST' ) {
|
||||
require ExtUtils::Manifest;
|
||||
my $manifest = ExtUtils::Manifest::maniread('MANIFEST');
|
||||
|
||||
$args{EXE_FILES} =
|
||||
[ grep { exists $manifest->{$_} } @{ $args{EXE_FILES} } ];
|
||||
}
|
||||
|
||||
$args{test}{TESTS} ||= 't/*.t';
|
||||
$args{test}{TESTS} = join( ' ',
|
||||
grep { !exists( $DisabledTests{$_} ) }
|
||||
map { glob($_) } split( /\s+/, $args{test}{TESTS} ) );
|
||||
|
||||
my $missing = join( ',', @Missing );
|
||||
my $config =
|
||||
join( ',', UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} )
|
||||
if $Config;
|
||||
|
||||
$PostambleActions = (
|
||||
($missing and not $UnderCPAN)
|
||||
? "\$(PERL) $0 --config=$config --installdeps=$missing"
|
||||
: "\$(NOECHO) \$(NOOP)"
|
||||
);
|
||||
|
||||
my $deps_list = join( ',', @Missing, @Existing );
|
||||
|
||||
$PostambleActionsUpgradeDeps =
|
||||
"\$(PERL) $0 --config=$config --upgradedeps=$deps_list";
|
||||
|
||||
my $config_notest =
|
||||
join( ',', (UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config}),
|
||||
'notest', 1 )
|
||||
if $Config;
|
||||
|
||||
$PostambleActionsNoTest = (
|
||||
($missing and not $UnderCPAN)
|
||||
? "\$(PERL) $0 --config=$config_notest --installdeps=$missing"
|
||||
: "\$(NOECHO) \$(NOOP)"
|
||||
);
|
||||
|
||||
$PostambleActionsUpgradeDepsNoTest =
|
||||
"\$(PERL) $0 --config=$config_notest --upgradedeps=$deps_list";
|
||||
|
||||
$PostambleActionsListDeps =
|
||||
'@$(PERL) -le "print for @ARGV" '
|
||||
. join(' ', map $Missing[$_], grep $_ % 2 == 0, 0..$#Missing);
|
||||
|
||||
my @all = (@Missing, @Existing);
|
||||
|
||||
$PostambleActionsListAllDeps =
|
||||
'@$(PERL) -le "print for @ARGV" '
|
||||
. join(' ', map $all[$_], grep $_ % 2 == 0, 0..$#all);
|
||||
|
||||
return %args;
|
||||
}
|
||||
|
||||
# a wrapper to ExtUtils::MakeMaker::WriteMakefile
|
||||
sub Write {
|
||||
require Carp;
|
||||
Carp::croak "WriteMakefile: Need even number of args" if @_ % 2;
|
||||
|
||||
if ($CheckOnly) {
|
||||
print << ".";
|
||||
*** Makefile not written in check-only mode.
|
||||
.
|
||||
return;
|
||||
}
|
||||
|
||||
my %args = _make_args(@_);
|
||||
|
||||
no strict 'refs';
|
||||
|
||||
$PostambleUsed = 0;
|
||||
local *MY::postamble = \&postamble unless defined &MY::postamble;
|
||||
ExtUtils::MakeMaker::WriteMakefile(%args);
|
||||
|
||||
print << "." unless $PostambleUsed;
|
||||
*** WARNING: Makefile written with customized MY::postamble() without
|
||||
including contents from Module::AutoInstall::postamble() --
|
||||
auto installation features disabled. Please contact the author.
|
||||
.
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub postamble {
|
||||
$PostambleUsed = 1;
|
||||
my $fragment;
|
||||
|
||||
$fragment .= <<"AUTO_INSTALL" if !$InstallDepsTarget;
|
||||
|
||||
config :: installdeps
|
||||
\t\$(NOECHO) \$(NOOP)
|
||||
AUTO_INSTALL
|
||||
|
||||
$fragment .= <<"END_MAKE";
|
||||
|
||||
checkdeps ::
|
||||
\t\$(PERL) $0 --checkdeps
|
||||
|
||||
installdeps ::
|
||||
\t$PostambleActions
|
||||
|
||||
installdeps_notest ::
|
||||
\t$PostambleActionsNoTest
|
||||
|
||||
upgradedeps ::
|
||||
\t$PostambleActionsUpgradeDeps
|
||||
|
||||
upgradedeps_notest ::
|
||||
\t$PostambleActionsUpgradeDepsNoTest
|
||||
|
||||
listdeps ::
|
||||
\t$PostambleActionsListDeps
|
||||
|
||||
listalldeps ::
|
||||
\t$PostambleActionsListAllDeps
|
||||
|
||||
END_MAKE
|
||||
|
||||
return $fragment;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
#line 1178
|
@ -0,0 +1,470 @@
|
||||
#line 1
|
||||
package Module::Install;
|
||||
|
||||
# For any maintainers:
|
||||
# The load order for Module::Install is a bit magic.
|
||||
# It goes something like this...
|
||||
#
|
||||
# IF ( host has Module::Install installed, creating author mode ) {
|
||||
# 1. Makefile.PL calls "use inc::Module::Install"
|
||||
# 2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install
|
||||
# 3. The installed version of inc::Module::Install loads
|
||||
# 4. inc::Module::Install calls "require Module::Install"
|
||||
# 5. The ./inc/ version of Module::Install loads
|
||||
# } ELSE {
|
||||
# 1. Makefile.PL calls "use inc::Module::Install"
|
||||
# 2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install
|
||||
# 3. The ./inc/ version of Module::Install loads
|
||||
# }
|
||||
|
||||
use 5.005;
|
||||
use strict 'vars';
|
||||
use Cwd ();
|
||||
use File::Find ();
|
||||
use File::Path ();
|
||||
|
||||
use vars qw{$VERSION $MAIN};
|
||||
BEGIN {
|
||||
# All Module::Install core packages now require synchronised versions.
|
||||
# This will be used to ensure we don't accidentally load old or
|
||||
# different versions of modules.
|
||||
# This is not enforced yet, but will be some time in the next few
|
||||
# releases once we can make sure it won't clash with custom
|
||||
# Module::Install extensions.
|
||||
$VERSION = '1.04';
|
||||
|
||||
# Storage for the pseudo-singleton
|
||||
$MAIN = undef;
|
||||
|
||||
*inc::Module::Install::VERSION = *VERSION;
|
||||
@inc::Module::Install::ISA = __PACKAGE__;
|
||||
|
||||
}
|
||||
|
||||
sub import {
|
||||
my $class = shift;
|
||||
my $self = $class->new(@_);
|
||||
my $who = $self->_caller;
|
||||
|
||||
#-------------------------------------------------------------
|
||||
# all of the following checks should be included in import(),
|
||||
# to allow "eval 'require Module::Install; 1' to test
|
||||
# installation of Module::Install. (RT #51267)
|
||||
#-------------------------------------------------------------
|
||||
|
||||
# Whether or not inc::Module::Install is actually loaded, the
|
||||
# $INC{inc/Module/Install.pm} is what will still get set as long as
|
||||
# the caller loaded module this in the documented manner.
|
||||
# If not set, the caller may NOT have loaded the bundled version, and thus
|
||||
# they may not have a MI version that works with the Makefile.PL. This would
|
||||
# result in false errors or unexpected behaviour. And we don't want that.
|
||||
my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm';
|
||||
unless ( $INC{$file} ) { die <<"END_DIE" }
|
||||
|
||||
Please invoke ${\__PACKAGE__} with:
|
||||
|
||||
use inc::${\__PACKAGE__};
|
||||
|
||||
not:
|
||||
|
||||
use ${\__PACKAGE__};
|
||||
|
||||
END_DIE
|
||||
|
||||
# This reportedly fixes a rare Win32 UTC file time issue, but
|
||||
# as this is a non-cross-platform XS module not in the core,
|
||||
# we shouldn't really depend on it. See RT #24194 for detail.
|
||||
# (Also, this module only supports Perl 5.6 and above).
|
||||
eval "use Win32::UTCFileTime" if $^O eq 'MSWin32' && $] >= 5.006;
|
||||
|
||||
# If the script that is loading Module::Install is from the future,
|
||||
# then make will detect this and cause it to re-run over and over
|
||||
# again. This is bad. Rather than taking action to touch it (which
|
||||
# is unreliable on some platforms and requires write permissions)
|
||||
# for now we should catch this and refuse to run.
|
||||
if ( -f $0 ) {
|
||||
my $s = (stat($0))[9];
|
||||
|
||||
# If the modification time is only slightly in the future,
|
||||
# sleep briefly to remove the problem.
|
||||
my $a = $s - time;
|
||||
if ( $a > 0 and $a < 5 ) { sleep 5 }
|
||||
|
||||
# Too far in the future, throw an error.
|
||||
my $t = time;
|
||||
if ( $s > $t ) { die <<"END_DIE" }
|
||||
|
||||
Your installer $0 has a modification time in the future ($s > $t).
|
||||
|
||||
This is known to create infinite loops in make.
|
||||
|
||||
Please correct this, then run $0 again.
|
||||
|
||||
END_DIE
|
||||
}
|
||||
|
||||
|
||||
# Build.PL was formerly supported, but no longer is due to excessive
|
||||
# difficulty in implementing every single feature twice.
|
||||
if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" }
|
||||
|
||||
Module::Install no longer supports Build.PL.
|
||||
|
||||
It was impossible to maintain duel backends, and has been deprecated.
|
||||
|
||||
Please remove all Build.PL files and only use the Makefile.PL installer.
|
||||
|
||||
END_DIE
|
||||
|
||||
#-------------------------------------------------------------
|
||||
|
||||
# To save some more typing in Module::Install installers, every...
|
||||
# use inc::Module::Install
|
||||
# ...also acts as an implicit use strict.
|
||||
$^H |= strict::bits(qw(refs subs vars));
|
||||
|
||||
#-------------------------------------------------------------
|
||||
|
||||
unless ( -f $self->{file} ) {
|
||||
foreach my $key (keys %INC) {
|
||||
delete $INC{$key} if $key =~ /Module\/Install/;
|
||||
}
|
||||
|
||||
local $^W;
|
||||
require "$self->{path}/$self->{dispatch}.pm";
|
||||
File::Path::mkpath("$self->{prefix}/$self->{author}");
|
||||
$self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self );
|
||||
$self->{admin}->init;
|
||||
@_ = ($class, _self => $self);
|
||||
goto &{"$self->{name}::import"};
|
||||
}
|
||||
|
||||
local $^W;
|
||||
*{"${who}::AUTOLOAD"} = $self->autoload;
|
||||
$self->preload;
|
||||
|
||||
# Unregister loader and worker packages so subdirs can use them again
|
||||
delete $INC{'inc/Module/Install.pm'};
|
||||
delete $INC{'Module/Install.pm'};
|
||||
|
||||
# Save to the singleton
|
||||
$MAIN = $self;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub autoload {
|
||||
my $self = shift;
|
||||
my $who = $self->_caller;
|
||||
my $cwd = Cwd::cwd();
|
||||
my $sym = "${who}::AUTOLOAD";
|
||||
$sym->{$cwd} = sub {
|
||||
my $pwd = Cwd::cwd();
|
||||
if ( my $code = $sym->{$pwd} ) {
|
||||
# Delegate back to parent dirs
|
||||
goto &$code unless $cwd eq $pwd;
|
||||
}
|
||||
unless ($$sym =~ s/([^:]+)$//) {
|
||||
# XXX: it looks like we can't retrieve the missing function
|
||||
# via $$sym (usually $main::AUTOLOAD) in this case.
|
||||
# I'm still wondering if we should slurp Makefile.PL to
|
||||
# get some context or not ...
|
||||
my ($package, $file, $line) = caller;
|
||||
die <<"EOT";
|
||||
Unknown function is found at $file line $line.
|
||||
Execution of $file aborted due to runtime errors.
|
||||
|
||||
If you're a contributor to a project, you may need to install
|
||||
some Module::Install extensions from CPAN (or other repository).
|
||||
If you're a user of a module, please contact the author.
|
||||
EOT
|
||||
}
|
||||
my $method = $1;
|
||||
if ( uc($method) eq $method ) {
|
||||
# Do nothing
|
||||
return;
|
||||
} elsif ( $method =~ /^_/ and $self->can($method) ) {
|
||||
# Dispatch to the root M:I class
|
||||
return $self->$method(@_);
|
||||
}
|
||||
|
||||
# Dispatch to the appropriate plugin
|
||||
unshift @_, ( $self, $1 );
|
||||
goto &{$self->can('call')};
|
||||
};
|
||||
}
|
||||
|
||||
sub preload {
|
||||
my $self = shift;
|
||||
unless ( $self->{extensions} ) {
|
||||
$self->load_extensions(
|
||||
"$self->{prefix}/$self->{path}", $self
|
||||
);
|
||||
}
|
||||
|
||||
my @exts = @{$self->{extensions}};
|
||||
unless ( @exts ) {
|
||||
@exts = $self->{admin}->load_all_extensions;
|
||||
}
|
||||
|
||||
my %seen;
|
||||
foreach my $obj ( @exts ) {
|
||||
while (my ($method, $glob) = each %{ref($obj) . '::'}) {
|
||||
next unless $obj->can($method);
|
||||
next if $method =~ /^_/;
|
||||
next if $method eq uc($method);
|
||||
$seen{$method}++;
|
||||
}
|
||||
}
|
||||
|
||||
my $who = $self->_caller;
|
||||
foreach my $name ( sort keys %seen ) {
|
||||
local $^W;
|
||||
*{"${who}::$name"} = sub {
|
||||
${"${who}::AUTOLOAD"} = "${who}::$name";
|
||||
goto &{"${who}::AUTOLOAD"};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
sub new {
|
||||
my ($class, %args) = @_;
|
||||
|
||||
delete $INC{'FindBin.pm'};
|
||||
{
|
||||
# to suppress the redefine warning
|
||||
local $SIG{__WARN__} = sub {};
|
||||
require FindBin;
|
||||
}
|
||||
|
||||
# ignore the prefix on extension modules built from top level.
|
||||
my $base_path = Cwd::abs_path($FindBin::Bin);
|
||||
unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) {
|
||||
delete $args{prefix};
|
||||
}
|
||||
return $args{_self} if $args{_self};
|
||||
|
||||
$args{dispatch} ||= 'Admin';
|
||||
$args{prefix} ||= 'inc';
|
||||
$args{author} ||= ($^O eq 'VMS' ? '_author' : '.author');
|
||||
$args{bundle} ||= 'inc/BUNDLES';
|
||||
$args{base} ||= $base_path;
|
||||
$class =~ s/^\Q$args{prefix}\E:://;
|
||||
$args{name} ||= $class;
|
||||
$args{version} ||= $class->VERSION;
|
||||
unless ( $args{path} ) {
|
||||
$args{path} = $args{name};
|
||||
$args{path} =~ s!::!/!g;
|
||||
}
|
||||
$args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm";
|
||||
$args{wrote} = 0;
|
||||
|
||||
bless( \%args, $class );
|
||||
}
|
||||
|
||||
sub call {
|
||||
my ($self, $method) = @_;
|
||||
my $obj = $self->load($method) or return;
|
||||
splice(@_, 0, 2, $obj);
|
||||
goto &{$obj->can($method)};
|
||||
}
|
||||
|
||||
sub load {
|
||||
my ($self, $method) = @_;
|
||||
|
||||
$self->load_extensions(
|
||||
"$self->{prefix}/$self->{path}", $self
|
||||
) unless $self->{extensions};
|
||||
|
||||
foreach my $obj (@{$self->{extensions}}) {
|
||||
return $obj if $obj->can($method);
|
||||
}
|
||||
|
||||
my $admin = $self->{admin} or die <<"END_DIE";
|
||||
The '$method' method does not exist in the '$self->{prefix}' path!
|
||||
Please remove the '$self->{prefix}' directory and run $0 again to load it.
|
||||
END_DIE
|
||||
|
||||
my $obj = $admin->load($method, 1);
|
||||
push @{$self->{extensions}}, $obj;
|
||||
|
||||
$obj;
|
||||
}
|
||||
|
||||
sub load_extensions {
|
||||
my ($self, $path, $top) = @_;
|
||||
|
||||
my $should_reload = 0;
|
||||
unless ( grep { ! ref $_ and lc $_ eq lc $self->{prefix} } @INC ) {
|
||||
unshift @INC, $self->{prefix};
|
||||
$should_reload = 1;
|
||||
}
|
||||
|
||||
foreach my $rv ( $self->find_extensions($path) ) {
|
||||
my ($file, $pkg) = @{$rv};
|
||||
next if $self->{pathnames}{$pkg};
|
||||
|
||||
local $@;
|
||||
my $new = eval { local $^W; require $file; $pkg->can('new') };
|
||||
unless ( $new ) {
|
||||
warn $@ if $@;
|
||||
next;
|
||||
}
|
||||
$self->{pathnames}{$pkg} =
|
||||
$should_reload ? delete $INC{$file} : $INC{$file};
|
||||
push @{$self->{extensions}}, &{$new}($pkg, _top => $top );
|
||||
}
|
||||
|
||||
$self->{extensions} ||= [];
|
||||
}
|
||||
|
||||
sub find_extensions {
|
||||
my ($self, $path) = @_;
|
||||
|
||||
my @found;
|
||||
File::Find::find( sub {
|
||||
my $file = $File::Find::name;
|
||||
return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is;
|
||||
my $subpath = $1;
|
||||
return if lc($subpath) eq lc($self->{dispatch});
|
||||
|
||||
$file = "$self->{path}/$subpath.pm";
|
||||
my $pkg = "$self->{name}::$subpath";
|
||||
$pkg =~ s!/!::!g;
|
||||
|
||||
# If we have a mixed-case package name, assume case has been preserved
|
||||
# correctly. Otherwise, root through the file to locate the case-preserved
|
||||
# version of the package name.
|
||||
if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) {
|
||||
my $content = Module::Install::_read($subpath . '.pm');
|
||||
my $in_pod = 0;
|
||||
foreach ( split //, $content ) {
|
||||
$in_pod = 1 if /^=\w/;
|
||||
$in_pod = 0 if /^=cut/;
|
||||
next if ($in_pod || /^=cut/); # skip pod text
|
||||
next if /^\s*#/; # and comments
|
||||
if ( m/^\s*package\s+($pkg)\s*;/i ) {
|
||||
$pkg = $1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
push @found, [ $file, $pkg ];
|
||||
}, $path ) if -d $path;
|
||||
|
||||
@found;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#####################################################################
|
||||
# Common Utility Functions
|
||||
|
||||
sub _caller {
|
||||
my $depth = 0;
|
||||
my $call = caller($depth);
|
||||
while ( $call eq __PACKAGE__ ) {
|
||||
$depth++;
|
||||
$call = caller($depth);
|
||||
}
|
||||
return $call;
|
||||
}
|
||||
|
||||
# Done in evals to avoid confusing Perl::MinimumVersion
|
||||
eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@;
|
||||
sub _read {
|
||||
local *FH;
|
||||
open( FH, '<', $_[0] ) or die "open($_[0]): $!";
|
||||
my $string = do { local $/; <FH> };
|
||||
close FH or die "close($_[0]): $!";
|
||||
return $string;
|
||||
}
|
||||
END_NEW
|
||||
sub _read {
|
||||
local *FH;
|
||||
open( FH, "< $_[0]" ) or die "open($_[0]): $!";
|
||||
my $string = do { local $/; <FH> };
|
||||
close FH or die "close($_[0]): $!";
|
||||
return $string;
|
||||
}
|
||||
END_OLD
|
||||
|
||||
sub _readperl {
|
||||
my $string = Module::Install::_read($_[0]);
|
||||
$string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg;
|
||||
$string =~ s/(\n)\n*__(?:DATA|END)__\b.*\z/$1/s;
|
||||
$string =~ s/\n\n=\w+.+?\n\n=cut\b.+?\n+/\n\n/sg;
|
||||
return $string;
|
||||
}
|
||||
|
||||
sub _readpod {
|
||||
my $string = Module::Install::_read($_[0]);
|
||||
$string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg;
|
||||
return $string if $_[0] =~ /\.pod\z/;
|
||||
$string =~ s/(^|\n=cut\b.+?\n+)[^=\s].+?\n(\n=\w+|\z)/$1$2/sg;
|
||||
$string =~ s/\n*=pod\b[^\n]*\n+/\n\n/sg;
|
||||
$string =~ s/\n*=cut\b[^\n]*\n+/\n\n/sg;
|
||||
$string =~ s/^\n+//s;
|
||||
return $string;
|
||||
}
|
||||
|
||||
# Done in evals to avoid confusing Perl::MinimumVersion
|
||||
eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@;
|
||||
sub _write {
|
||||
local *FH;
|
||||
open( FH, '>', $_[0] ) or die "open($_[0]): $!";
|
||||
foreach ( 1 .. $#_ ) {
|
||||
print FH $_[$_] or die "print($_[0]): $!";
|
||||
}
|
||||
close FH or die "close($_[0]): $!";
|
||||
}
|
||||
END_NEW
|
||||
sub _write {
|
||||
local *FH;
|
||||
open( FH, "> $_[0]" ) or die "open($_[0]): $!";
|
||||
foreach ( 1 .. $#_ ) {
|
||||
print FH $_[$_] or die "print($_[0]): $!";
|
||||
}
|
||||
close FH or die "close($_[0]): $!";
|
||||
}
|
||||
END_OLD
|
||||
|
||||
# _version is for processing module versions (eg, 1.03_05) not
|
||||
# Perl versions (eg, 5.8.1).
|
||||
sub _version ($) {
|
||||
my $s = shift || 0;
|
||||
my $d =()= $s =~ /(\.)/g;
|
||||
if ( $d >= 2 ) {
|
||||
# Normalise multipart versions
|
||||
$s =~ s/(\.)(\d{1,3})/sprintf("$1%03d",$2)/eg;
|
||||
}
|
||||
$s =~ s/^(\d+)\.?//;
|
||||
my $l = $1 || 0;
|
||||
my @v = map {
|
||||
$_ . '0' x (3 - length $_)
|
||||
} $s =~ /(\d{1,3})\D?/g;
|
||||
$l = $l . '.' . join '', @v if @v;
|
||||
return $l + 0;
|
||||
}
|
||||
|
||||
sub _cmp ($$) {
|
||||
_version($_[1]) <=> _version($_[2]);
|
||||
}
|
||||
|
||||
# Cloned from Params::Util::_CLASS
|
||||
sub _CLASS ($) {
|
||||
(
|
||||
defined $_[0]
|
||||
and
|
||||
! ref $_[0]
|
||||
and
|
||||
$_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s
|
||||
) ? $_[0] : undef;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
# Copyright 2008 - 2011 Adam Kennedy.
|
@ -0,0 +1,93 @@
|
||||
#line 1
|
||||
package Module::Install::AutoInstall;
|
||||
|
||||
use strict;
|
||||
use Module::Install::Base ();
|
||||
|
||||
use vars qw{$VERSION @ISA $ISCORE};
|
||||
BEGIN {
|
||||
$VERSION = '1.04';
|
||||
@ISA = 'Module::Install::Base';
|
||||
$ISCORE = 1;
|
||||
}
|
||||
|
||||
sub AutoInstall { $_[0] }
|
||||
|
||||
sub run {
|
||||
my $self = shift;
|
||||
$self->auto_install_now(@_);
|
||||
}
|
||||
|
||||
sub write {
|
||||
my $self = shift;
|
||||
$self->auto_install(@_);
|
||||
}
|
||||
|
||||
sub auto_install {
|
||||
my $self = shift;
|
||||
return if $self->{done}++;
|
||||
|
||||
# Flatten array of arrays into a single array
|
||||
my @core = map @$_, map @$_, grep ref,
|
||||
$self->build_requires, $self->requires;
|
||||
|
||||
my @config = @_;
|
||||
|
||||
# We'll need Module::AutoInstall
|
||||
$self->include('Module::AutoInstall');
|
||||
require Module::AutoInstall;
|
||||
|
||||
my @features_require = Module::AutoInstall->import(
|
||||
(@config ? (-config => \@config) : ()),
|
||||
(@core ? (-core => \@core) : ()),
|
||||
$self->features,
|
||||
);
|
||||
|
||||
my %seen;
|
||||
my @requires = map @$_, map @$_, grep ref, $self->requires;
|
||||
while (my ($mod, $ver) = splice(@requires, 0, 2)) {
|
||||
$seen{$mod}{$ver}++;
|
||||
}
|
||||
my @build_requires = map @$_, map @$_, grep ref, $self->build_requires;
|
||||
while (my ($mod, $ver) = splice(@build_requires, 0, 2)) {
|
||||
$seen{$mod}{$ver}++;
|
||||
}
|
||||
my @configure_requires = map @$_, map @$_, grep ref, $self->configure_requires;
|
||||
while (my ($mod, $ver) = splice(@configure_requires, 0, 2)) {
|
||||
$seen{$mod}{$ver}++;
|
||||
}
|
||||
|
||||
my @deduped;
|
||||
while (my ($mod, $ver) = splice(@features_require, 0, 2)) {
|
||||
push @deduped, $mod => $ver unless $seen{$mod}{$ver}++;
|
||||
}
|
||||
|
||||
$self->requires(@deduped);
|
||||
|
||||
$self->makemaker_args( Module::AutoInstall::_make_args() );
|
||||
|
||||
my $class = ref($self);
|
||||
$self->postamble(
|
||||
"# --- $class section:\n" .
|
||||
Module::AutoInstall::postamble()
|
||||
);
|
||||
}
|
||||
|
||||
sub installdeps_target {
|
||||
my ($self, @args) = @_;
|
||||
|
||||
$self->include('Module::AutoInstall');
|
||||
require Module::AutoInstall;
|
||||
|
||||
Module::AutoInstall::_installdeps_target(1);
|
||||
|
||||
$self->auto_install(@args);
|
||||
}
|
||||
|
||||
sub auto_install_now {
|
||||
my $self = shift;
|
||||
$self->auto_install(@_);
|
||||
Module::AutoInstall::do_install();
|
||||
}
|
||||
|
||||
1;
|
@ -0,0 +1,83 @@
|
||||
#line 1
|
||||
package Module::Install::Base;
|
||||
|
||||
use strict 'vars';
|
||||
use vars qw{$VERSION};
|
||||
BEGIN {
|
||||
$VERSION = '1.04';
|
||||
}
|
||||
|
||||
# Suspend handler for "redefined" warnings
|
||||
BEGIN {
|
||||
my $w = $SIG{__WARN__};
|
||||
$SIG{__WARN__} = sub { $w };
|
||||
}
|
||||
|
||||
#line 42
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
unless ( defined &{"${class}::call"} ) {
|
||||
*{"${class}::call"} = sub { shift->_top->call(@_) };
|
||||
}
|
||||
unless ( defined &{"${class}::load"} ) {
|
||||
*{"${class}::load"} = sub { shift->_top->load(@_) };
|
||||
}
|
||||
bless { @_ }, $class;
|
||||
}
|
||||
|
||||
#line 61
|
||||
|
||||
sub AUTOLOAD {
|
||||
local $@;
|
||||
my $func = eval { shift->_top->autoload } or return;
|
||||
goto &$func;
|
||||
}
|
||||
|
||||
#line 75
|
||||
|
||||
sub _top {
|
||||
$_[0]->{_top};
|
||||
}
|
||||
|
||||
#line 90
|
||||
|
||||
sub admin {
|
||||
$_[0]->_top->{admin}
|
||||
or
|
||||
Module::Install::Base::FakeAdmin->new;
|
||||
}
|
||||
|
||||
#line 106
|
||||
|
||||
sub is_admin {
|
||||
! $_[0]->admin->isa('Module::Install::Base::FakeAdmin');
|
||||
}
|
||||
|
||||
sub DESTROY {}
|
||||
|
||||
package Module::Install::Base::FakeAdmin;
|
||||
|
||||
use vars qw{$VERSION};
|
||||
BEGIN {
|
||||
$VERSION = $Module::Install::Base::VERSION;
|
||||
}
|
||||
|
||||
my $fake;
|
||||
|
||||
sub new {
|
||||
$fake ||= bless(\@_, $_[0]);
|
||||
}
|
||||
|
||||
sub AUTOLOAD {}
|
||||
|
||||
sub DESTROY {}
|
||||
|
||||
# Restore warning handler
|
||||
BEGIN {
|
||||
$SIG{__WARN__} = $SIG{__WARN__}->();
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
#line 159
|
@ -0,0 +1,81 @@
|
||||
#line 1
|
||||
package Module::Install::Can;
|
||||
|
||||
use strict;
|
||||
use Config ();
|
||||
use File::Spec ();
|
||||
use ExtUtils::MakeMaker ();
|
||||
use Module::Install::Base ();
|
||||
|
||||
use vars qw{$VERSION @ISA $ISCORE};
|
||||
BEGIN {
|
||||
$VERSION = '1.04';
|
||||
@ISA = 'Module::Install::Base';
|
||||
$ISCORE = 1;
|
||||
}
|
||||
|
||||
# check if we can load some module
|
||||
### Upgrade this to not have to load the module if possible
|
||||
sub can_use {
|
||||
my ($self, $mod, $ver) = @_;
|
||||
$mod =~ s{::|\\}{/}g;
|
||||
$mod .= '.pm' unless $mod =~ /\.pm$/i;
|
||||
|
||||
my $pkg = $mod;
|
||||
$pkg =~ s{/}{::}g;
|
||||
$pkg =~ s{\.pm$}{}i;
|
||||
|
||||
local $@;
|
||||
eval { require $mod; $pkg->VERSION($ver || 0); 1 };
|
||||
}
|
||||
|
||||
# check if we can run some command
|
||||
sub can_run {
|
||||
my ($self, $cmd) = @_;
|
||||
|
||||
my $_cmd = $cmd;
|
||||
return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd));
|
||||
|
||||
for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') {
|
||||
next if $dir eq '';
|
||||
my $abs = File::Spec->catfile($dir, $_[1]);
|
||||
return $abs if (-x $abs or $abs = MM->maybe_command($abs));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
# can we locate a (the) C compiler
|
||||
sub can_cc {
|
||||
my $self = shift;
|
||||
my @chunks = split(/ /, $Config::Config{cc}) or return;
|
||||
|
||||
# $Config{cc} may contain args; try to find out the program part
|
||||
while (@chunks) {
|
||||
return $self->can_run("@chunks") || (pop(@chunks), next);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
# Fix Cygwin bug on maybe_command();
|
||||
if ( $^O eq 'cygwin' ) {
|
||||
require ExtUtils::MM_Cygwin;
|
||||
require ExtUtils::MM_Win32;
|
||||
if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) {
|
||||
*ExtUtils::MM_Cygwin::maybe_command = sub {
|
||||
my ($self, $file) = @_;
|
||||
if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) {
|
||||
ExtUtils::MM_Win32->maybe_command($file);
|
||||
} else {
|
||||
ExtUtils::MM_Unix->maybe_command($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
#line 156
|
@ -0,0 +1,93 @@
|
||||
#line 1
|
||||
package Module::Install::Fetch;
|
||||
|
||||
use strict;
|
||||
use Module::Install::Base ();
|
||||
|
||||
use vars qw{$VERSION @ISA $ISCORE};
|
||||
BEGIN {
|
||||
$VERSION = '1.04';
|
||||
@ISA = 'Module::Install::Base';
|
||||
$ISCORE = 1;
|
||||
}
|
||||
|
||||
sub get_file {
|
||||
my ($self, %args) = @_;
|
||||
my ($scheme, $host, $path, $file) =
|
||||
$args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return;
|
||||
|
||||
if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) {
|
||||
$args{url} = $args{ftp_url}
|
||||
or (warn("LWP support unavailable!\n"), return);
|
||||
($scheme, $host, $path, $file) =
|
||||
$args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return;
|
||||
}
|
||||
|
||||
$|++;
|
||||
print "Fetching '$file' from $host... ";
|
||||
|
||||
unless (eval { require Socket; Socket::inet_aton($host) }) {
|
||||
warn "'$host' resolve failed!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
return unless $scheme eq 'ftp' or $scheme eq 'http';
|
||||
|
||||
require Cwd;
|
||||
my $dir = Cwd::getcwd();
|
||||
chdir $args{local_dir} or return if exists $args{local_dir};
|
||||
|
||||
if (eval { require LWP::Simple; 1 }) {
|
||||
LWP::Simple::mirror($args{url}, $file);
|
||||
}
|
||||
elsif (eval { require Net::FTP; 1 }) { eval {
|
||||
# use Net::FTP to get past firewall
|
||||
my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600);
|
||||
$ftp->login("anonymous", 'anonymous@example.com');
|
||||
$ftp->cwd($path);
|
||||
$ftp->binary;
|
||||
$ftp->get($file) or (warn("$!\n"), return);
|
||||
$ftp->quit;
|
||||
} }
|
||||
elsif (my $ftp = $self->can_run('ftp')) { eval {
|
||||
# no Net::FTP, fallback to ftp.exe
|
||||
require FileHandle;
|
||||
my $fh = FileHandle->new;
|
||||
|
||||
local $SIG{CHLD} = 'IGNORE';
|
||||
unless ($fh->open("|$ftp -n")) {
|
||||
warn "Couldn't open ftp: $!\n";
|
||||
chdir $dir; return;
|
||||
}
|
||||
|
||||
my @dialog = split(/\n/, <<"END_FTP");
|
||||
open $host
|
||||
user anonymous anonymous\@example.com
|
||||
cd $path
|
||||
binary
|
||||
get $file $file
|
||||
quit
|
||||
END_FTP
|
||||
foreach (@dialog) { $fh->print("$_\n") }
|
||||
$fh->close;
|
||||
} }
|
||||
else {
|
||||
warn "No working 'ftp' program available!\n";
|
||||
chdir $dir; return;
|
||||
}
|
||||
|
||||
unless (-f $file) {
|
||||
warn "Fetching failed: $@\n";
|
||||
chdir $dir; return;
|
||||
}
|
||||
|
||||
return if exists $args{size} and -s $file != $args{size};
|
||||
system($args{run}) if exists $args{run};
|
||||
unlink($file) if $args{remove};
|
||||
|
||||
print(((!exists $args{check_for} or -e $args{check_for})
|
||||
? "done!" : "failed! ($!)"), "\n");
|
||||
chdir $dir; return !$?;
|
||||
}
|
||||
|
||||
1;
|
@ -0,0 +1,34 @@
|
||||
#line 1
|
||||
package Module::Install::Include;
|
||||
|
||||
use strict;
|
||||
use Module::Install::Base ();
|
||||
|
||||
use vars qw{$VERSION @ISA $ISCORE};
|
||||
BEGIN {
|
||||
$VERSION = '1.04';
|
||||
@ISA = 'Module::Install::Base';
|
||||
$ISCORE = 1;
|
||||
}
|
||||
|
||||
sub include {
|
||||
shift()->admin->include(@_);
|
||||
}
|
||||
|
||||
sub include_deps {
|
||||
shift()->admin->include_deps(@_);
|
||||
}
|
||||
|
||||
sub auto_include {
|
||||
shift()->admin->auto_include(@_);
|
||||
}
|
||||
|
||||
sub auto_include_deps {
|
||||
shift()->admin->auto_include_deps(@_);
|
||||
}
|
||||
|
||||
sub auto_include_dependent_dists {
|
||||
shift()->admin->auto_include_dependent_dists(@_);
|
||||
}
|
||||
|
||||
1;
|
@ -0,0 +1,414 @@
|
||||
#line 1
|
||||
package Module::Install::Makefile;
|
||||
|
||||
use strict 'vars';
|
||||
use ExtUtils::MakeMaker ();
|
||||
use Module::Install::Base ();
|
||||
use Fcntl qw/:flock :seek/;
|
||||
|
||||
use vars qw{$VERSION @ISA $ISCORE};
|
||||
BEGIN {
|
||||
$VERSION = '1.04';
|
||||
@ISA = 'Module::Install::Base';
|
||||
$ISCORE = 1;
|
||||
}
|
||||
|
||||
sub Makefile { $_[0] }
|
||||
|
||||
my %seen = ();
|
||||
|
||||
sub prompt {
|
||||
shift;
|
||||
|
||||
# Infinite loop protection
|
||||
my @c = caller();
|
||||
if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) {
|
||||
die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])";
|
||||
}
|
||||
|
||||
# In automated testing or non-interactive session, always use defaults
|
||||
if ( ($ENV{AUTOMATED_TESTING} or -! -t STDIN) and ! $ENV{PERL_MM_USE_DEFAULT} ) {
|
||||
local $ENV{PERL_MM_USE_DEFAULT} = 1;
|
||||
goto &ExtUtils::MakeMaker::prompt;
|
||||
} else {
|
||||
goto &ExtUtils::MakeMaker::prompt;
|
||||
}
|
||||
}
|
||||
|
||||
# Store a cleaned up version of the MakeMaker version,
|
||||
# since we need to behave differently in a variety of
|
||||
# ways based on the MM version.
|
||||
my $makemaker = eval $ExtUtils::MakeMaker::VERSION;
|
||||
|
||||
# If we are passed a param, do a "newer than" comparison.
|
||||
# Otherwise, just return the MakeMaker version.
|
||||
sub makemaker {
|
||||
( @_ < 2 or $makemaker >= eval($_[1]) ) ? $makemaker : 0
|
||||
}
|
||||
|
||||
# Ripped from ExtUtils::MakeMaker 6.56, and slightly modified
|
||||
# as we only need to know here whether the attribute is an array
|
||||
# or a hash or something else (which may or may not be appendable).
|
||||
my %makemaker_argtype = (
|
||||
C => 'ARRAY',
|
||||
CONFIG => 'ARRAY',
|
||||
# CONFIGURE => 'CODE', # ignore
|
||||
DIR => 'ARRAY',
|
||||
DL_FUNCS => 'HASH',
|
||||
DL_VARS => 'ARRAY',
|
||||
EXCLUDE_EXT => 'ARRAY',
|
||||
EXE_FILES => 'ARRAY',
|
||||
FUNCLIST => 'ARRAY',
|
||||
H => 'ARRAY',
|
||||
IMPORTS => 'HASH',
|
||||
INCLUDE_EXT => 'ARRAY',
|
||||
LIBS => 'ARRAY', # ignore ''
|
||||
MAN1PODS => 'HASH',
|
||||
MAN3PODS => 'HASH',
|
||||
META_ADD => 'HASH',
|
||||
META_MERGE => 'HASH',
|
||||
PL_FILES => 'HASH',
|
||||
PM => 'HASH',
|
||||
PMLIBDIRS => 'ARRAY',
|
||||
PMLIBPARENTDIRS => 'ARRAY',
|
||||
PREREQ_PM => 'HASH',
|
||||
CONFIGURE_REQUIRES => 'HASH',
|
||||
SKIP => 'ARRAY',
|
||||
TYPEMAPS => 'ARRAY',
|
||||
XS => 'HASH',
|
||||
# VERSION => ['version',''], # ignore
|
||||
# _KEEP_AFTER_FLUSH => '',
|
||||
|
||||
clean => 'HASH',
|
||||
depend => 'HASH',
|
||||
dist => 'HASH',
|
||||
dynamic_lib=> 'HASH',
|
||||
linkext => 'HASH',
|
||||
macro => 'HASH',
|
||||
postamble => 'HASH',
|
||||
realclean => 'HASH',
|
||||
test => 'HASH',
|
||||
tool_autosplit => 'HASH',
|
||||
|
||||
# special cases where you can use makemaker_append
|
||||
CCFLAGS => 'APPENDABLE',
|
||||
DEFINE => 'APPENDABLE',
|
||||
INC => 'APPENDABLE',
|
||||
LDDLFLAGS => 'APPENDABLE',
|
||||
LDFROM => 'APPENDABLE',
|
||||
);
|
||||
|
||||
sub makemaker_args {
|
||||
my ($self, %new_args) = @_;
|
||||
my $args = ( $self->{makemaker_args} ||= {} );
|
||||
foreach my $key (keys %new_args) {
|
||||
if ($makemaker_argtype{$key}) {
|
||||
if ($makemaker_argtype{$key} eq 'ARRAY') {
|
||||
$args->{$key} = [] unless defined $args->{$key};
|
||||
unless (ref $args->{$key} eq 'ARRAY') {
|
||||
$args->{$key} = [$args->{$key}]
|
||||
}
|
||||
push @{$args->{$key}},
|
||||
ref $new_args{$key} eq 'ARRAY'
|
||||
? @{$new_args{$key}}
|
||||
: $new_args{$key};
|
||||
}
|
||||
elsif ($makemaker_argtype{$key} eq 'HASH') {
|
||||
$args->{$key} = {} unless defined $args->{$key};
|
||||
foreach my $skey (keys %{ $new_args{$key} }) {
|
||||
$args->{$key}{$skey} = $new_args{$key}{$skey};
|
||||
}
|
||||
}
|
||||
elsif ($makemaker_argtype{$key} eq 'APPENDABLE') {
|
||||
$self->makemaker_append($key => $new_args{$key});
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (defined $args->{$key}) {
|
||||
warn qq{MakeMaker attribute "$key" is overriden; use "makemaker_append" to append values\n};
|
||||
}
|
||||
$args->{$key} = $new_args{$key};
|
||||
}
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
|
||||
# For mm args that take multiple space-seperated args,
|
||||
# append an argument to the current list.
|
||||
sub makemaker_append {
|
||||
my $self = shift;
|
||||
my $name = shift;
|
||||
my $args = $self->makemaker_args;
|
||||
$args->{$name} = defined $args->{$name}
|
||||
? join( ' ', $args->{$name}, @_ )
|
||||
: join( ' ', @_ );
|
||||
}
|
||||
|
||||
sub build_subdirs {
|
||||
my $self = shift;
|
||||
my $subdirs = $self->makemaker_args->{DIR} ||= [];
|
||||
for my $subdir (@_) {
|
||||
push @$subdirs, $subdir;
|
||||
}
|
||||
}
|
||||
|
||||
sub clean_files {
|
||||
my $self = shift;
|
||||
my $clean = $self->makemaker_args->{clean} ||= {};
|
||||
%$clean = (
|
||||
%$clean,
|
||||
FILES => join ' ', grep { length $_ } ($clean->{FILES} || (), @_),
|
||||
);
|
||||
}
|
||||
|
||||
sub realclean_files {
|
||||
my $self = shift;
|
||||
my $realclean = $self->makemaker_args->{realclean} ||= {};
|
||||
%$realclean = (
|
||||
%$realclean,
|
||||
FILES => join ' ', grep { length $_ } ($realclean->{FILES} || (), @_),
|
||||
);
|
||||
}
|
||||
|
||||
sub libs {
|
||||
my $self = shift;
|
||||
my $libs = ref $_[0] ? shift : [ shift ];
|
||||
$self->makemaker_args( LIBS => $libs );
|
||||
}
|
||||
|
||||
sub inc {
|
||||
my $self = shift;
|
||||
$self->makemaker_args( INC => shift );
|
||||
}
|
||||
|
||||
sub _wanted_t {
|
||||
}
|
||||
|
||||
sub tests_recursive {
|
||||
my $self = shift;
|
||||
my $dir = shift || 't';
|
||||
unless ( -d $dir ) {
|
||||
die "tests_recursive dir '$dir' does not exist";
|
||||
}
|
||||
my %tests = map { $_ => 1 } split / /, ($self->tests || '');
|
||||
require File::Find;
|
||||
File::Find::find(
|
||||
sub { /\.t$/ and -f $_ and $tests{"$File::Find::dir/*.t"} = 1 },
|
||||
$dir
|
||||
);
|
||||
$self->tests( join ' ', sort keys %tests );
|
||||
}
|
||||
|
||||
sub write {
|
||||
my $self = shift;
|
||||
die "&Makefile->write() takes no arguments\n" if @_;
|
||||
|
||||
# Check the current Perl version
|
||||
my $perl_version = $self->perl_version;
|
||||
if ( $perl_version ) {
|
||||
eval "use $perl_version; 1"
|
||||
or die "ERROR: perl: Version $] is installed, "
|
||||
. "but we need version >= $perl_version";
|
||||
}
|
||||
|
||||
# Make sure we have a new enough MakeMaker
|
||||
require ExtUtils::MakeMaker;
|
||||
|
||||
if ( $perl_version and $self->_cmp($perl_version, '5.006') >= 0 ) {
|
||||
# MakeMaker can complain about module versions that include
|
||||
# an underscore, even though its own version may contain one!
|
||||
# Hence the funny regexp to get rid of it. See RT #35800
|
||||
# for details.
|
||||
my ($v) = $ExtUtils::MakeMaker::VERSION =~ /^(\d+\.\d+)/;
|
||||
$self->build_requires( 'ExtUtils::MakeMaker' => $v );
|
||||
$self->configure_requires( 'ExtUtils::MakeMaker' => $v );
|
||||
} else {
|
||||
# Allow legacy-compatibility with 5.005 by depending on the
|
||||
# most recent EU:MM that supported 5.005.
|
||||
$self->build_requires( 'ExtUtils::MakeMaker' => 6.36 );
|
||||
$self->configure_requires( 'ExtUtils::MakeMaker' => 6.36 );
|
||||
}
|
||||
|
||||
# Generate the MakeMaker params
|
||||
my $args = $self->makemaker_args;
|
||||
$args->{DISTNAME} = $self->name;
|
||||
$args->{NAME} = $self->module_name || $self->name;
|
||||
$args->{NAME} =~ s/-/::/g;
|
||||
$args->{VERSION} = $self->version or die <<'EOT';
|
||||
ERROR: Can't determine distribution version. Please specify it
|
||||
explicitly via 'version' in Makefile.PL, or set a valid $VERSION
|
||||
in a module, and provide its file path via 'version_from' (or
|
||||
'all_from' if you prefer) in Makefile.PL.
|
||||
EOT
|
||||
|
||||
if ( $self->tests ) {
|
||||
my @tests = split ' ', $self->tests;
|
||||
my %seen;
|
||||
$args->{test} = {
|
||||
TESTS => (join ' ', grep {!$seen{$_}++} @tests),
|
||||
};
|
||||
} elsif ( $Module::Install::ExtraTests::use_extratests ) {
|
||||
# Module::Install::ExtraTests doesn't set $self->tests and does its own tests via harness.
|
||||
# So, just ignore our xt tests here.
|
||||
} elsif ( -d 'xt' and ($Module::Install::AUTHOR or $ENV{RELEASE_TESTING}) ) {
|
||||
$args->{test} = {
|
||||
TESTS => join( ' ', map { "$_/*.t" } grep { -d $_ } qw{ t xt } ),
|
||||
};
|
||||
}
|
||||
if ( $] >= 5.005 ) {
|
||||
$args->{ABSTRACT} = $self->abstract;
|
||||
$args->{AUTHOR} = join ', ', @{$self->author || []};
|
||||
}
|
||||
if ( $self->makemaker(6.10) ) {
|
||||
$args->{NO_META} = 1;
|
||||
#$args->{NO_MYMETA} = 1;
|
||||
}
|
||||
if ( $self->makemaker(6.17) and $self->sign ) {
|
||||
$args->{SIGN} = 1;
|
||||
}
|
||||
unless ( $self->is_admin ) {
|
||||
delete $args->{SIGN};
|
||||
}
|
||||
if ( $self->makemaker(6.31) and $self->license ) {
|
||||
$args->{LICENSE} = $self->license;
|
||||
}
|
||||
|
||||
my $prereq = ($args->{PREREQ_PM} ||= {});
|
||||
%$prereq = ( %$prereq,
|
||||
map { @$_ } # flatten [module => version]
|
||||
map { @$_ }
|
||||
grep $_,
|
||||
($self->requires)
|
||||
);
|
||||
|
||||
# Remove any reference to perl, PREREQ_PM doesn't support it
|
||||
delete $args->{PREREQ_PM}->{perl};
|
||||
|
||||
# Merge both kinds of requires into BUILD_REQUIRES
|
||||
my $build_prereq = ($args->{BUILD_REQUIRES} ||= {});
|
||||
%$build_prereq = ( %$build_prereq,
|
||||
map { @$_ } # flatten [module => version]
|
||||
map { @$_ }
|
||||
grep $_,
|
||||
($self->configure_requires, $self->build_requires)
|
||||
);
|
||||
|
||||
# Remove any reference to perl, BUILD_REQUIRES doesn't support it
|
||||
delete $args->{BUILD_REQUIRES}->{perl};
|
||||
|
||||
# Delete bundled dists from prereq_pm, add it to Makefile DIR
|
||||
my $subdirs = ($args->{DIR} || []);
|
||||
if ($self->bundles) {
|
||||
my %processed;
|
||||
foreach my $bundle (@{ $self->bundles }) {
|
||||
my ($mod_name, $dist_dir) = @$bundle;
|
||||
delete $prereq->{$mod_name};
|
||||
$dist_dir = File::Basename::basename($dist_dir); # dir for building this module
|
||||
if (not exists $processed{$dist_dir}) {
|
||||
if (-d $dist_dir) {
|
||||
# List as sub-directory to be processed by make
|
||||
push @$subdirs, $dist_dir;
|
||||
}
|
||||
# Else do nothing: the module is already present on the system
|
||||
$processed{$dist_dir} = undef;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unless ( $self->makemaker('6.55_03') ) {
|
||||
%$prereq = (%$prereq,%$build_prereq);
|
||||
delete $args->{BUILD_REQUIRES};
|
||||
}
|
||||
|
||||
if ( my $perl_version = $self->perl_version ) {
|
||||
eval "use $perl_version; 1"
|
||||
or die "ERROR: perl: Version $] is installed, "
|
||||
. "but we need version >= $perl_version";
|
||||
|
||||
if ( $self->makemaker(6.48) ) {
|
||||
$args->{MIN_PERL_VERSION} = $perl_version;
|
||||
}
|
||||
}
|
||||
|
||||
if ($self->installdirs) {
|
||||
warn qq{old INSTALLDIRS (probably set by makemaker_args) is overriden by installdirs\n} if $args->{INSTALLDIRS};
|
||||
$args->{INSTALLDIRS} = $self->installdirs;
|
||||
}
|
||||
|
||||
my %args = map {
|
||||
( $_ => $args->{$_} ) } grep {defined($args->{$_} )
|
||||
} keys %$args;
|
||||
|
||||
my $user_preop = delete $args{dist}->{PREOP};
|
||||
if ( my $preop = $self->admin->preop($user_preop) ) {
|
||||
foreach my $key ( keys %$preop ) {
|
||||
$args{dist}->{$key} = $preop->{$key};
|
||||
}
|
||||
}
|
||||
|
||||
my $mm = ExtUtils::MakeMaker::WriteMakefile(%args);
|
||||
$self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile');
|
||||
}
|
||||
|
||||
sub fix_up_makefile {
|
||||
my $self = shift;
|
||||
my $makefile_name = shift;
|
||||
my $top_class = ref($self->_top) || '';
|
||||
my $top_version = $self->_top->VERSION || '';
|
||||
|
||||
my $preamble = $self->preamble
|
||||
? "# Preamble by $top_class $top_version\n"
|
||||
. $self->preamble
|
||||
: '';
|
||||
my $postamble = "# Postamble by $top_class $top_version\n"
|
||||
. ($self->postamble || '');
|
||||
|
||||
local *MAKEFILE;
|
||||
open MAKEFILE, "+< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
|
||||
eval { flock MAKEFILE, LOCK_EX };
|
||||
my $makefile = do { local $/; <MAKEFILE> };
|
||||
|
||||
$makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /;
|
||||
$makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g;
|
||||
$makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g;
|
||||
$makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m;
|
||||
$makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m;
|
||||
|
||||
# Module::Install will never be used to build the Core Perl
|
||||
# Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks
|
||||
# PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist
|
||||
$makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m;
|
||||
#$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m;
|
||||
|
||||
# Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well.
|
||||
$makefile =~ s/(\"?)-I\$\(PERL_LIB\)\1//g;
|
||||
|
||||
# XXX - This is currently unused; not sure if it breaks other MM-users
|
||||
# $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg;
|
||||
|
||||
seek MAKEFILE, 0, SEEK_SET;
|
||||
truncate MAKEFILE, 0;
|
||||
print MAKEFILE "$preamble$makefile$postamble" or die $!;
|
||||
close MAKEFILE or die $!;
|
||||
|
||||
1;
|
||||
}
|
||||
|
||||
sub preamble {
|
||||
my ($self, $text) = @_;
|
||||
$self->{preamble} = $text . $self->{preamble} if defined $text;
|
||||
$self->{preamble};
|
||||
}
|
||||
|
||||
sub postamble {
|
||||
my ($self, $text) = @_;
|
||||
$self->{postamble} ||= $self->admin->postamble;
|
||||
$self->{postamble} .= $text if defined $text;
|
||||
$self->{postamble}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
#line 540
|
@ -0,0 +1,722 @@
|
||||
#line 1
|
||||
package Module::Install::Metadata;
|
||||
|
||||
use strict 'vars';
|
||||
use Module::Install::Base ();
|
||||
|
||||
use vars qw{$VERSION @ISA $ISCORE};
|
||||
BEGIN {
|
||||
$VERSION = '1.04';
|
||||
@ISA = 'Module::Install::Base';
|
||||
$ISCORE = 1;
|
||||
}
|
||||
|
||||
my @boolean_keys = qw{
|
||||
sign
|
||||
};
|
||||
|
||||
my @scalar_keys = qw{
|
||||
name
|
||||
module_name
|
||||
abstract
|
||||
version
|
||||
distribution_type
|
||||
tests
|
||||
installdirs
|
||||
};
|
||||
|
||||
my @tuple_keys = qw{
|
||||
configure_requires
|
||||
build_requires
|
||||
requires
|
||||
recommends
|
||||
bundles
|
||||
resources
|
||||
};
|
||||
|
||||
my @resource_keys = qw{
|
||||
homepage
|
||||
bugtracker
|
||||
repository
|
||||
};
|
||||
|
||||
my @array_keys = qw{
|
||||
keywords
|
||||
author
|
||||
};
|
||||
|
||||
*authors = \&author;
|
||||
|
||||
sub Meta { shift }
|
||||
sub Meta_BooleanKeys { @boolean_keys }
|
||||
sub Meta_ScalarKeys { @scalar_keys }
|
||||
sub Meta_TupleKeys { @tuple_keys }
|
||||
sub Meta_ResourceKeys { @resource_keys }
|
||||
sub Meta_ArrayKeys { @array_keys }
|
||||
|
||||
foreach my $key ( @boolean_keys ) {
|
||||
*$key = sub {
|
||||
my $self = shift;
|
||||
if ( defined wantarray and not @_ ) {
|
||||
return $self->{values}->{$key};
|
||||
}
|
||||
$self->{values}->{$key} = ( @_ ? $_[0] : 1 );
|
||||
return $self;
|
||||
};
|
||||
}
|
||||
|
||||
foreach my $key ( @scalar_keys ) {
|
||||
*$key = sub {
|
||||
my $self = shift;
|
||||
return $self->{values}->{$key} if defined wantarray and !@_;
|
||||
$self->{values}->{$key} = shift;
|
||||
return $self;
|
||||
};
|
||||
}
|
||||
|
||||
foreach my $key ( @array_keys ) {
|
||||
*$key = sub {
|
||||
my $self = shift;
|
||||
return $self->{values}->{$key} if defined wantarray and !@_;
|
||||
$self->{values}->{$key} ||= [];
|
||||
push @{$self->{values}->{$key}}, @_;
|
||||
return $self;
|
||||
};
|
||||
}
|
||||
|
||||
foreach my $key ( @resource_keys ) {
|
||||
*$key = sub {
|
||||
my $self = shift;
|
||||
unless ( @_ ) {
|
||||
return () unless $self->{values}->{resources};
|
||||
return map { $_->[1] }
|
||||
grep { $_->[0] eq $key }
|
||||
@{ $self->{values}->{resources} };
|
||||
}
|
||||
return $self->{values}->{resources}->{$key} unless @_;
|
||||
my $uri = shift or die(
|
||||
"Did not provide a value to $key()"
|
||||
);
|
||||
$self->resources( $key => $uri );
|
||||
return 1;
|
||||
};
|
||||
}
|
||||
|
||||
foreach my $key ( grep { $_ ne "resources" } @tuple_keys) {
|
||||
*$key = sub {
|
||||
my $self = shift;
|
||||
return $self->{values}->{$key} unless @_;
|
||||
my @added;
|
||||
while ( @_ ) {
|
||||
my $module = shift or last;
|
||||
my $version = shift || 0;
|
||||
push @added, [ $module, $version ];
|
||||
}
|
||||
push @{ $self->{values}->{$key} }, @added;
|
||||
return map {@$_} @added;
|
||||
};
|
||||
}
|
||||
|
||||
# Resource handling
|
||||
my %lc_resource = map { $_ => 1 } qw{
|
||||
homepage
|
||||
license
|
||||
bugtracker
|
||||
repository
|
||||
};
|
||||
|
||||
sub resources {
|
||||
my $self = shift;
|
||||
while ( @_ ) {
|
||||
my $name = shift or last;
|
||||
my $value = shift or next;
|
||||
if ( $name eq lc $name and ! $lc_resource{$name} ) {
|
||||
die("Unsupported reserved lowercase resource '$name'");
|
||||
}
|
||||
$self->{values}->{resources} ||= [];
|
||||
push @{ $self->{values}->{resources} }, [ $name, $value ];
|
||||
}
|
||||
$self->{values}->{resources};
|
||||
}
|
||||
|
||||
# Aliases for build_requires that will have alternative
|
||||
# meanings in some future version of META.yml.
|
||||
sub test_requires { shift->build_requires(@_) }
|
||||
sub install_requires { shift->build_requires(@_) }
|
||||
|
||||
# Aliases for installdirs options
|
||||
sub install_as_core { $_[0]->installdirs('perl') }
|
||||
sub install_as_cpan { $_[0]->installdirs('site') }
|
||||
sub install_as_site { $_[0]->installdirs('site') }
|
||||
sub install_as_vendor { $_[0]->installdirs('vendor') }
|
||||
|
||||
sub dynamic_config {
|
||||
my $self = shift;
|
||||
my $value = @_ ? shift : 1;
|
||||
if ( $self->{values}->{dynamic_config} ) {
|
||||
# Once dynamic we never change to static, for safety
|
||||
return 0;
|
||||
}
|
||||
$self->{values}->{dynamic_config} = $value ? 1 : 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Convenience command
|
||||
sub static_config {
|
||||
shift->dynamic_config(0);
|
||||
}
|
||||
|
||||
sub perl_version {
|
||||
my $self = shift;
|
||||
return $self->{values}->{perl_version} unless @_;
|
||||
my $version = shift or die(
|
||||
"Did not provide a value to perl_version()"
|
||||
);
|
||||
|
||||
# Normalize the version
|
||||
$version = $self->_perl_version($version);
|
||||
|
||||
# We don't support the really old versions
|
||||
unless ( $version >= 5.005 ) {
|
||||
die "Module::Install only supports 5.005 or newer (use ExtUtils::MakeMaker)\n";
|
||||
}
|
||||
|
||||
$self->{values}->{perl_version} = $version;
|
||||
}
|
||||
|
||||
sub all_from {
|
||||
my ( $self, $file ) = @_;
|
||||
|
||||
unless ( defined($file) ) {
|
||||
my $name = $self->name or die(
|
||||
"all_from called with no args without setting name() first"
|
||||
);
|
||||
$file = join('/', 'lib', split(/-/, $name)) . '.pm';
|
||||
$file =~ s{.*/}{} unless -e $file;
|
||||
unless ( -e $file ) {
|
||||
die("all_from cannot find $file from $name");
|
||||
}
|
||||
}
|
||||
unless ( -f $file ) {
|
||||
die("The path '$file' does not exist, or is not a file");
|
||||
}
|
||||
|
||||
$self->{values}{all_from} = $file;
|
||||
|
||||
# Some methods pull from POD instead of code.
|
||||
# If there is a matching .pod, use that instead
|
||||
my $pod = $file;
|
||||
$pod =~ s/\.pm$/.pod/i;
|
||||
$pod = $file unless -e $pod;
|
||||
|
||||
# Pull the different values
|
||||
$self->name_from($file) unless $self->name;
|
||||
$self->version_from($file) unless $self->version;
|
||||
$self->perl_version_from($file) unless $self->perl_version;
|
||||
$self->author_from($pod) unless @{$self->author || []};
|
||||
$self->license_from($pod) unless $self->license;
|
||||
$self->abstract_from($pod) unless $self->abstract;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub provides {
|
||||
my $self = shift;
|
||||
my $provides = ( $self->{values}->{provides} ||= {} );
|
||||
%$provides = (%$provides, @_) if @_;
|
||||
return $provides;
|
||||
}
|
||||
|
||||
sub auto_provides {
|
||||
my $self = shift;
|
||||
return $self unless $self->is_admin;
|
||||
unless (-e 'MANIFEST') {
|
||||
warn "Cannot deduce auto_provides without a MANIFEST, skipping\n";
|
||||
return $self;
|
||||
}
|
||||
# Avoid spurious warnings as we are not checking manifest here.
|
||||
local $SIG{__WARN__} = sub {1};
|
||||
require ExtUtils::Manifest;
|
||||
local *ExtUtils::Manifest::manicheck = sub { return };
|
||||
|
||||
require Module::Build;
|
||||
my $build = Module::Build->new(
|
||||
dist_name => $self->name,
|
||||
dist_version => $self->version,
|
||||
license => $self->license,
|
||||
);
|
||||
$self->provides( %{ $build->find_dist_packages || {} } );
|
||||
}
|
||||
|
||||
sub feature {
|
||||
my $self = shift;
|
||||
my $name = shift;
|
||||
my $features = ( $self->{values}->{features} ||= [] );
|
||||
my $mods;
|
||||
|
||||
if ( @_ == 1 and ref( $_[0] ) ) {
|
||||
# The user used ->feature like ->features by passing in the second
|
||||
# argument as a reference. Accomodate for that.
|
||||
$mods = $_[0];
|
||||
} else {
|
||||
$mods = \@_;
|
||||
}
|
||||
|
||||
my $count = 0;
|
||||
push @$features, (
|
||||
$name => [
|
||||
map {
|
||||
ref($_) ? ( ref($_) eq 'HASH' ) ? %$_ : @$_ : $_
|
||||
} @$mods
|
||||
]
|
||||
);
|
||||
|
||||
return @$features;
|
||||
}
|
||||
|
||||
sub features {
|
||||
my $self = shift;
|
||||
while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) {
|
||||
$self->feature( $name, @$mods );
|
||||
}
|
||||
return $self->{values}->{features}
|
||||
? @{ $self->{values}->{features} }
|
||||
: ();
|
||||
}
|
||||
|
||||
sub no_index {
|
||||
my $self = shift;
|
||||
my $type = shift;
|
||||
push @{ $self->{values}->{no_index}->{$type} }, @_ if $type;
|
||||
return $self->{values}->{no_index};
|
||||
}
|
||||
|
||||
sub read {
|
||||
my $self = shift;
|
||||
$self->include_deps( 'YAML::Tiny', 0 );
|
||||
|
||||
require YAML::Tiny;
|
||||
my $data = YAML::Tiny::LoadFile('META.yml');
|
||||
|
||||
# Call methods explicitly in case user has already set some values.
|
||||
while ( my ( $key, $value ) = each %$data ) {
|
||||
next unless $self->can($key);
|
||||
if ( ref $value eq 'HASH' ) {
|
||||
while ( my ( $module, $version ) = each %$value ) {
|
||||
$self->can($key)->($self, $module => $version );
|
||||
}
|
||||
} else {
|
||||
$self->can($key)->($self, $value);
|
||||
}
|
||||
}
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub write {
|
||||
my $self = shift;
|
||||
return $self unless $self->is_admin;
|
||||
$self->admin->write_meta;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub version_from {
|
||||
require ExtUtils::MM_Unix;
|
||||
my ( $self, $file ) = @_;
|
||||
$self->version( ExtUtils::MM_Unix->parse_version($file) );
|
||||
|
||||
# for version integrity check
|
||||
$self->makemaker_args( VERSION_FROM => $file );
|
||||
}
|
||||
|
||||
sub abstract_from {
|
||||
require ExtUtils::MM_Unix;
|
||||
my ( $self, $file ) = @_;
|
||||
$self->abstract(
|
||||
bless(
|
||||
{ DISTNAME => $self->name },
|
||||
'ExtUtils::MM_Unix'
|
||||
)->parse_abstract($file)
|
||||
);
|
||||
}
|
||||
|
||||
# Add both distribution and module name
|
||||
sub name_from {
|
||||
my ($self, $file) = @_;
|
||||
if (
|
||||
Module::Install::_read($file) =~ m/
|
||||
^ \s*
|
||||
package \s*
|
||||
([\w:]+)
|
||||
\s* ;
|
||||
/ixms
|
||||
) {
|
||||
my ($name, $module_name) = ($1, $1);
|
||||
$name =~ s{::}{-}g;
|
||||
$self->name($name);
|
||||
unless ( $self->module_name ) {
|
||||
$self->module_name($module_name);
|
||||
}
|
||||
} else {
|
||||
die("Cannot determine name from $file\n");
|
||||
}
|
||||
}
|
||||
|
||||
sub _extract_perl_version {
|
||||
if (
|
||||
$_[0] =~ m/
|
||||
^\s*
|
||||
(?:use|require) \s*
|
||||
v?
|
||||
([\d_\.]+)
|
||||
\s* ;
|
||||
/ixms
|
||||
) {
|
||||
my $perl_version = $1;
|
||||
$perl_version =~ s{_}{}g;
|
||||
return $perl_version;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sub perl_version_from {
|
||||
my $self = shift;
|
||||
my $perl_version=_extract_perl_version(Module::Install::_read($_[0]));
|
||||
if ($perl_version) {
|
||||
$self->perl_version($perl_version);
|
||||
} else {
|
||||
warn "Cannot determine perl version info from $_[0]\n";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sub author_from {
|
||||
my $self = shift;
|
||||
my $content = Module::Install::_read($_[0]);
|
||||
if ($content =~ m/
|
||||
=head \d \s+ (?:authors?)\b \s*
|
||||
([^\n]*)
|
||||
|
|
||||
=head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s*
|
||||
.*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s*
|
||||
([^\n]*)
|
||||
/ixms) {
|
||||
my $author = $1 || $2;
|
||||
|
||||
# XXX: ugly but should work anyway...
|
||||
if (eval "require Pod::Escapes; 1") {
|
||||
# Pod::Escapes has a mapping table.
|
||||
# It's in core of perl >= 5.9.3, and should be installed
|
||||
# as one of the Pod::Simple's prereqs, which is a prereq
|
||||
# of Pod::Text 3.x (see also below).
|
||||
$author =~ s{ E<( (\d+) | ([A-Za-z]+) )> }
|
||||
{
|
||||
defined $2
|
||||
? chr($2)
|
||||
: defined $Pod::Escapes::Name2character_number{$1}
|
||||
? chr($Pod::Escapes::Name2character_number{$1})
|
||||
: do {
|
||||
warn "Unknown escape: E<$1>";
|
||||
"E<$1>";
|
||||
};
|
||||
}gex;
|
||||
}
|
||||
elsif (eval "require Pod::Text; 1" && $Pod::Text::VERSION < 3) {
|
||||
# Pod::Text < 3.0 has yet another mapping table,
|
||||
# though the table name of 2.x and 1.x are different.
|
||||
# (1.x is in core of Perl < 5.6, 2.x is in core of
|
||||
# Perl < 5.9.3)
|
||||
my $mapping = ($Pod::Text::VERSION < 2)
|
||||
? \%Pod::Text::HTML_Escapes
|
||||
: \%Pod::Text::ESCAPES;
|
||||
$author =~ s{ E<( (\d+) | ([A-Za-z]+) )> }
|
||||
{
|
||||
defined $2
|
||||
? chr($2)
|
||||
: defined $mapping->{$1}
|
||||
? $mapping->{$1}
|
||||
: do {
|
||||
warn "Unknown escape: E<$1>";
|
||||
"E<$1>";
|
||||
};
|
||||
}gex;
|
||||
}
|
||||
else {
|
||||
$author =~ s{E<lt>}{<}g;
|
||||
$author =~ s{E<gt>}{>}g;
|
||||
}
|
||||
$self->author($author);
|
||||
} else {
|
||||
warn "Cannot determine author info from $_[0]\n";
|
||||
}
|
||||
}
|
||||
|
||||
#Stolen from M::B
|
||||
my %license_urls = (
|
||||
perl => 'http://dev.perl.org/licenses/',
|
||||
apache => 'http://apache.org/licenses/LICENSE-2.0',
|
||||
apache_1_1 => 'http://apache.org/licenses/LICENSE-1.1',
|
||||
artistic => 'http://opensource.org/licenses/artistic-license.php',
|
||||
artistic_2 => 'http://opensource.org/licenses/artistic-license-2.0.php',
|
||||
lgpl => 'http://opensource.org/licenses/lgpl-license.php',
|
||||
lgpl2 => 'http://opensource.org/licenses/lgpl-2.1.php',
|
||||
lgpl3 => 'http://opensource.org/licenses/lgpl-3.0.html',
|
||||
bsd => 'http://opensource.org/licenses/bsd-license.php',
|
||||
gpl => 'http://opensource.org/licenses/gpl-license.php',
|
||||
gpl2 => 'http://opensource.org/licenses/gpl-2.0.php',
|
||||
gpl3 => 'http://opensource.org/licenses/gpl-3.0.html',
|
||||
mit => 'http://opensource.org/licenses/mit-license.php',
|
||||
mozilla => 'http://opensource.org/licenses/mozilla1.1.php',
|
||||
open_source => undef,
|
||||
unrestricted => undef,
|
||||
restrictive => undef,
|
||||
unknown => undef,
|
||||
);
|
||||
|
||||
sub license {
|
||||
my $self = shift;
|
||||
return $self->{values}->{license} unless @_;
|
||||
my $license = shift or die(
|
||||
'Did not provide a value to license()'
|
||||
);
|
||||
$license = __extract_license($license) || lc $license;
|
||||
$self->{values}->{license} = $license;
|
||||
|
||||
# Automatically fill in license URLs
|
||||
if ( $license_urls{$license} ) {
|
||||
$self->resources( license => $license_urls{$license} );
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub _extract_license {
|
||||
my $pod = shift;
|
||||
my $matched;
|
||||
return __extract_license(
|
||||
($matched) = $pod =~ m/
|
||||
(=head \d \s+ L(?i:ICEN[CS]E|ICENSING)\b.*?)
|
||||
(=head \d.*|=cut.*|)\z
|
||||
/xms
|
||||
) || __extract_license(
|
||||
($matched) = $pod =~ m/
|
||||
(=head \d \s+ (?:C(?i:OPYRIGHTS?)|L(?i:EGAL))\b.*?)
|
||||
(=head \d.*|=cut.*|)\z
|
||||
/xms
|
||||
);
|
||||
}
|
||||
|
||||
sub __extract_license {
|
||||
my $license_text = shift or return;
|
||||
my @phrases = (
|
||||
'(?:under )?the same (?:terms|license) as (?:perl|the perl (?:\d )?programming language)' => 'perl', 1,
|
||||
'(?:under )?the terms of (?:perl|the perl programming language) itself' => 'perl', 1,
|
||||
'Artistic and GPL' => 'perl', 1,
|
||||
'GNU general public license' => 'gpl', 1,
|
||||
'GNU public license' => 'gpl', 1,
|
||||
'GNU lesser general public license' => 'lgpl', 1,
|
||||
'GNU lesser public license' => 'lgpl', 1,
|
||||
'GNU library general public license' => 'lgpl', 1,
|
||||
'GNU library public license' => 'lgpl', 1,
|
||||
'GNU Free Documentation license' => 'unrestricted', 1,
|
||||
'GNU Affero General Public License' => 'open_source', 1,
|
||||
'(?:Free)?BSD license' => 'bsd', 1,
|
||||
'Artistic license 2\.0' => 'artistic_2', 1,
|
||||
'Artistic license' => 'artistic', 1,
|
||||
'Apache (?:Software )?license' => 'apache', 1,
|
||||
'GPL' => 'gpl', 1,
|
||||
'LGPL' => 'lgpl', 1,
|
||||
'BSD' => 'bsd', 1,
|
||||
'Artistic' => 'artistic', 1,
|
||||
'MIT' => 'mit', 1,
|
||||
'Mozilla Public License' => 'mozilla', 1,
|
||||
'Q Public License' => 'open_source', 1,
|
||||
'OpenSSL License' => 'unrestricted', 1,
|
||||
'SSLeay License' => 'unrestricted', 1,
|
||||
'zlib License' => 'open_source', 1,
|
||||
'proprietary' => 'proprietary', 0,
|
||||
);
|
||||
while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) {
|
||||
$pattern =~ s#\s+#\\s+#gs;
|
||||
if ( $license_text =~ /\b$pattern\b/i ) {
|
||||
return $license;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
sub license_from {
|
||||
my $self = shift;
|
||||
if (my $license=_extract_license(Module::Install::_read($_[0]))) {
|
||||
$self->license($license);
|
||||
} else {
|
||||
warn "Cannot determine license info from $_[0]\n";
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
sub _extract_bugtracker {
|
||||
my @links = $_[0] =~ m#L<(
|
||||
https?\Q://rt.cpan.org/\E[^>]+|
|
||||
https?\Q://github.com/\E[\w_]+/[\w_]+/issues|
|
||||
https?\Q://code.google.com/p/\E[\w_\-]+/issues/list
|
||||
)>#gx;
|
||||
my %links;
|
||||
@links{@links}=();
|
||||
@links=keys %links;
|
||||
return @links;
|
||||
}
|
||||
|
||||
sub bugtracker_from {
|
||||
my $self = shift;
|
||||
my $content = Module::Install::_read($_[0]);
|
||||
my @links = _extract_bugtracker($content);
|
||||
unless ( @links ) {
|
||||
warn "Cannot determine bugtracker info from $_[0]\n";
|
||||
return 0;
|
||||
}
|
||||
if ( @links > 1 ) {
|
||||
warn "Found more than one bugtracker link in $_[0]\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
# Set the bugtracker
|
||||
bugtracker( $links[0] );
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub requires_from {
|
||||
my $self = shift;
|
||||
my $content = Module::Install::_readperl($_[0]);
|
||||
my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+(v?[\d\.]+)/mg;
|
||||
while ( @requires ) {
|
||||
my $module = shift @requires;
|
||||
my $version = shift @requires;
|
||||
$self->requires( $module => $version );
|
||||
}
|
||||
}
|
||||
|
||||
sub test_requires_from {
|
||||
my $self = shift;
|
||||
my $content = Module::Install::_readperl($_[0]);
|
||||
my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg;
|
||||
while ( @requires ) {
|
||||
my $module = shift @requires;
|
||||
my $version = shift @requires;
|
||||
$self->test_requires( $module => $version );
|
||||
}
|
||||
}
|
||||
|
||||
# Convert triple-part versions (eg, 5.6.1 or 5.8.9) to
|
||||
# numbers (eg, 5.006001 or 5.008009).
|
||||
# Also, convert double-part versions (eg, 5.8)
|
||||
sub _perl_version {
|
||||
my $v = $_[-1];
|
||||
$v =~ s/^([1-9])\.([1-9]\d?\d?)$/sprintf("%d.%03d",$1,$2)/e;
|
||||
$v =~ s/^([1-9])\.([1-9]\d?\d?)\.(0|[1-9]\d?\d?)$/sprintf("%d.%03d%03d",$1,$2,$3 || 0)/e;
|
||||
$v =~ s/(\.\d\d\d)000$/$1/;
|
||||
$v =~ s/_.+$//;
|
||||
if ( ref($v) ) {
|
||||
# Numify
|
||||
$v = $v + 0;
|
||||
}
|
||||
return $v;
|
||||
}
|
||||
|
||||
sub add_metadata {
|
||||
my $self = shift;
|
||||
my %hash = @_;
|
||||
for my $key (keys %hash) {
|
||||
warn "add_metadata: $key is not prefixed with 'x_'.\n" .
|
||||
"Use appopriate function to add non-private metadata.\n" unless $key =~ /^x_/;
|
||||
$self->{values}->{$key} = $hash{$key};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
######################################################################
|
||||
# MYMETA Support
|
||||
|
||||
sub WriteMyMeta {
|
||||
die "WriteMyMeta has been deprecated";
|
||||
}
|
||||
|
||||
sub write_mymeta_yaml {
|
||||
my $self = shift;
|
||||
|
||||
# We need YAML::Tiny to write the MYMETA.yml file
|
||||
unless ( eval { require YAML::Tiny; 1; } ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Generate the data
|
||||
my $meta = $self->_write_mymeta_data or return 1;
|
||||
|
||||
# Save as the MYMETA.yml file
|
||||
print "Writing MYMETA.yml\n";
|
||||
YAML::Tiny::DumpFile('MYMETA.yml', $meta);
|
||||
}
|
||||
|
||||
sub write_mymeta_json {
|
||||
my $self = shift;
|
||||
|
||||
# We need JSON to write the MYMETA.json file
|
||||
unless ( eval { require JSON; 1; } ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Generate the data
|
||||
my $meta = $self->_write_mymeta_data or return 1;
|
||||
|
||||
# Save as the MYMETA.yml file
|
||||
print "Writing MYMETA.json\n";
|
||||
Module::Install::_write(
|
||||
'MYMETA.json',
|
||||
JSON->new->pretty(1)->canonical->encode($meta),
|
||||
);
|
||||
}
|
||||
|
||||
sub _write_mymeta_data {
|
||||
my $self = shift;
|
||||
|
||||
# If there's no existing META.yml there is nothing we can do
|
||||
return undef unless -f 'META.yml';
|
||||
|
||||
# We need Parse::CPAN::Meta to load the file
|
||||
unless ( eval { require Parse::CPAN::Meta; 1; } ) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
# Merge the perl version into the dependencies
|
||||
my $val = $self->Meta->{values};
|
||||
my $perl = delete $val->{perl_version};
|
||||
if ( $perl ) {
|
||||
$val->{requires} ||= [];
|
||||
my $requires = $val->{requires};
|
||||
|
||||
# Canonize to three-dot version after Perl 5.6
|
||||
if ( $perl >= 5.006 ) {
|
||||
$perl =~ s{^(\d+)\.(\d\d\d)(\d*)}{join('.', $1, int($2||0), int($3||0))}e
|
||||
}
|
||||
unshift @$requires, [ perl => $perl ];
|
||||
}
|
||||
|
||||
# Load the advisory META.yml file
|
||||
my @yaml = Parse::CPAN::Meta::LoadFile('META.yml');
|
||||
my $meta = $yaml[0];
|
||||
|
||||
# Overwrite the non-configure dependency hashs
|
||||
delete $meta->{requires};
|
||||
delete $meta->{build_requires};
|
||||
delete $meta->{recommends};
|
||||
if ( exists $val->{requires} ) {
|
||||
$meta->{requires} = { map { @$_ } @{ $val->{requires} } };
|
||||
}
|
||||
if ( exists $val->{build_requires} ) {
|
||||
$meta->{build_requires} = { map { @$_ } @{ $val->{build_requires} } };
|
||||
}
|
||||
|
||||
return $meta;
|
||||
}
|
||||
|
||||
1;
|
@ -0,0 +1,29 @@
|
||||
#line 1
|
||||
package Module::Install::TestBase;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Module::Install::Base;
|
||||
|
||||
use vars qw($VERSION @ISA);
|
||||
BEGIN {
|
||||
$VERSION = '0.60';
|
||||
@ISA = 'Module::Install::Base';
|
||||
}
|
||||
|
||||
sub use_test_base {
|
||||
my $self = shift;
|
||||
$self->include('Test::Base');
|
||||
$self->include('Test::Base::Filter');
|
||||
$self->include('Spiffy');
|
||||
$self->include('Test::More');
|
||||
$self->include('Test::Builder');
|
||||
$self->include('Test::Builder::Module');
|
||||
$self->requires('Filter::Util::Call');
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=encoding utf8
|
||||
|
||||
#line 70
|
@ -0,0 +1,64 @@
|
||||
#line 1
|
||||
package Module::Install::Win32;
|
||||
|
||||
use strict;
|
||||
use Module::Install::Base ();
|
||||
|
||||
use vars qw{$VERSION @ISA $ISCORE};
|
||||
BEGIN {
|
||||
$VERSION = '1.04';
|
||||
@ISA = 'Module::Install::Base';
|
||||
$ISCORE = 1;
|
||||
}
|
||||
|
||||
# determine if the user needs nmake, and download it if needed
|
||||
sub check_nmake {
|
||||
my $self = shift;
|
||||
$self->load('can_run');
|
||||
$self->load('get_file');
|
||||
|
||||
require Config;
|
||||
return unless (
|
||||
$^O eq 'MSWin32' and
|
||||
$Config::Config{make} and
|
||||
$Config::Config{make} =~ /^nmake\b/i and
|
||||
! $self->can_run('nmake')
|
||||
);
|
||||
|
||||
print "The required 'nmake' executable not found, fetching it...\n";
|
||||
|
||||
require File::Basename;
|
||||
my $rv = $self->get_file(
|
||||
url => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe',
|
||||
ftp_url => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe',
|
||||
local_dir => File::Basename::dirname($^X),
|
||||
size => 51928,
|
||||
run => 'Nmake15.exe /o > nul',
|
||||
check_for => 'Nmake.exe',
|
||||
remove => 1,
|
||||
);
|
||||
|
||||
die <<'END_MESSAGE' unless $rv;
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Since you are using Microsoft Windows, you will need the 'nmake' utility
|
||||
before installation. It's available at:
|
||||
|
||||
http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe
|
||||
or
|
||||
ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe
|
||||
|
||||
Please download the file manually, save it to a directory in %PATH% (e.g.
|
||||
C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to
|
||||
that directory, and run "Nmake15.exe" from there; that will create the
|
||||
'nmake.exe' file needed by this module.
|
||||
|
||||
You may then resume the installation process described in README.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
END_MESSAGE
|
||||
|
||||
}
|
||||
|
||||
1;
|
@ -0,0 +1,63 @@
|
||||
#line 1
|
||||
package Module::Install::WriteAll;
|
||||
|
||||
use strict;
|
||||
use Module::Install::Base ();
|
||||
|
||||
use vars qw{$VERSION @ISA $ISCORE};
|
||||
BEGIN {
|
||||
$VERSION = '1.04';
|
||||
@ISA = qw{Module::Install::Base};
|
||||
$ISCORE = 1;
|
||||
}
|
||||
|
||||
sub WriteAll {
|
||||
my $self = shift;
|
||||
my %args = (
|
||||
meta => 1,
|
||||
sign => 0,
|
||||
inline => 0,
|
||||
check_nmake => 1,
|
||||
@_,
|
||||
);
|
||||
|
||||
$self->sign(1) if $args{sign};
|
||||
$self->admin->WriteAll(%args) if $self->is_admin;
|
||||
|
||||
$self->check_nmake if $args{check_nmake};
|
||||
unless ( $self->makemaker_args->{PL_FILES} ) {
|
||||
# XXX: This still may be a bit over-defensive...
|
||||
unless ($self->makemaker(6.25)) {
|
||||
$self->makemaker_args( PL_FILES => {} ) if -f 'Build.PL';
|
||||
}
|
||||
}
|
||||
|
||||
# Until ExtUtils::MakeMaker support MYMETA.yml, make sure
|
||||
# we clean it up properly ourself.
|
||||
$self->realclean_files('MYMETA.yml');
|
||||
|
||||
if ( $args{inline} ) {
|
||||
$self->Inline->write;
|
||||
} else {
|
||||
$self->Makefile->write;
|
||||
}
|
||||
|
||||
# The Makefile write process adds a couple of dependencies,
|
||||
# so write the META.yml files after the Makefile.
|
||||
if ( $args{meta} ) {
|
||||
$self->Meta->write;
|
||||
}
|
||||
|
||||
# Experimental support for MYMETA
|
||||
if ( $ENV{X_MYMETA} ) {
|
||||
if ( $ENV{X_MYMETA} eq 'JSON' ) {
|
||||
$self->Meta->write_mymeta_json;
|
||||
} else {
|
||||
$self->Meta->write_mymeta_yaml;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
1;
|
@ -0,0 +1,539 @@
|
||||
#line 1
|
||||
package Spiffy;
|
||||
use strict;
|
||||
use 5.006001;
|
||||
use warnings;
|
||||
use Carp;
|
||||
require Exporter;
|
||||
our $VERSION = '0.30';
|
||||
our @EXPORT = ();
|
||||
our @EXPORT_BASE = qw(field const stub super);
|
||||
our @EXPORT_OK = (@EXPORT_BASE, qw(id WWW XXX YYY ZZZ));
|
||||
our %EXPORT_TAGS = (XXX => [qw(WWW XXX YYY ZZZ)]);
|
||||
|
||||
my $stack_frame = 0;
|
||||
my $dump = 'yaml';
|
||||
my $bases_map = {};
|
||||
|
||||
sub WWW; sub XXX; sub YYY; sub ZZZ;
|
||||
|
||||
# This line is here to convince "autouse" into believing we are autousable.
|
||||
sub can {
|
||||
($_[1] eq 'import' and caller()->isa('autouse'))
|
||||
? \&Exporter::import # pacify autouse's equality test
|
||||
: $_[0]->SUPER::can($_[1]) # normal case
|
||||
}
|
||||
|
||||
# TODO
|
||||
#
|
||||
# Exported functions like field and super should be hidden so as not to
|
||||
# be confused with methods that can be inherited.
|
||||
#
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
$class = ref($class) || $class;
|
||||
my $self = bless {}, $class;
|
||||
while (@_) {
|
||||
my $method = shift;
|
||||
$self->$method(shift);
|
||||
}
|
||||
return $self;
|
||||
}
|
||||
|
||||
my $filtered_files = {};
|
||||
my $filter_dump = 0;
|
||||
my $filter_save = 0;
|
||||
our $filter_result = '';
|
||||
sub import {
|
||||
no strict 'refs';
|
||||
no warnings;
|
||||
my $self_package = shift;
|
||||
|
||||
# XXX Using parse_arguments here might cause confusion, because the
|
||||
# subclass's boolean_arguments and paired_arguments can conflict, causing
|
||||
# difficult debugging. Consider using something truly local.
|
||||
my ($args, @export_list) = do {
|
||||
local *boolean_arguments = sub {
|
||||
qw(
|
||||
-base -Base -mixin -selfless
|
||||
-XXX -dumper -yaml
|
||||
-filter_dump -filter_save
|
||||
)
|
||||
};
|
||||
local *paired_arguments = sub { qw(-package) };
|
||||
$self_package->parse_arguments(@_);
|
||||
};
|
||||
return spiffy_mixin_import(scalar(caller(0)), $self_package, @export_list)
|
||||
if $args->{-mixin};
|
||||
|
||||
$filter_dump = 1 if $args->{-filter_dump};
|
||||
$filter_save = 1 if $args->{-filter_save};
|
||||
$dump = 'yaml' if $args->{-yaml};
|
||||
$dump = 'dumper' if $args->{-dumper};
|
||||
|
||||
local @EXPORT_BASE = @EXPORT_BASE;
|
||||
|
||||
if ($args->{-XXX}) {
|
||||
push @EXPORT_BASE, @{$EXPORT_TAGS{XXX}}
|
||||
unless grep /^XXX$/, @EXPORT_BASE;
|
||||
}
|
||||
|
||||
spiffy_filter()
|
||||
if ($args->{-selfless} or $args->{-Base}) and
|
||||
not $filtered_files->{(caller($stack_frame))[1]}++;
|
||||
|
||||
my $caller_package = $args->{-package} || caller($stack_frame);
|
||||
push @{"$caller_package\::ISA"}, $self_package
|
||||
if $args->{-Base} or $args->{-base};
|
||||
|
||||
for my $class (@{all_my_bases($self_package)}) {
|
||||
next unless $class->isa('Spiffy');
|
||||
my @export = grep {
|
||||
not defined &{"$caller_package\::$_"};
|
||||
} ( @{"$class\::EXPORT"},
|
||||
($args->{-Base} or $args->{-base})
|
||||
? @{"$class\::EXPORT_BASE"} : (),
|
||||
);
|
||||
my @export_ok = grep {
|
||||
not defined &{"$caller_package\::$_"};
|
||||
} @{"$class\::EXPORT_OK"};
|
||||
|
||||
# Avoid calling the expensive Exporter::export
|
||||
# if there is nothing to do (optimization)
|
||||
my %exportable = map { ($_, 1) } @export, @export_ok;
|
||||
next unless keys %exportable;
|
||||
|
||||
my @export_save = @{"$class\::EXPORT"};
|
||||
my @export_ok_save = @{"$class\::EXPORT_OK"};
|
||||
@{"$class\::EXPORT"} = @export;
|
||||
@{"$class\::EXPORT_OK"} = @export_ok;
|
||||
my @list = grep {
|
||||
(my $v = $_) =~ s/^[\!\:]//;
|
||||
$exportable{$v} or ${"$class\::EXPORT_TAGS"}{$v};
|
||||
} @export_list;
|
||||
Exporter::export($class, $caller_package, @list);
|
||||
@{"$class\::EXPORT"} = @export_save;
|
||||
@{"$class\::EXPORT_OK"} = @export_ok_save;
|
||||
}
|
||||
}
|
||||
|
||||
sub spiffy_filter {
|
||||
require Filter::Util::Call;
|
||||
my $done = 0;
|
||||
Filter::Util::Call::filter_add(
|
||||
sub {
|
||||
return 0 if $done;
|
||||
my ($data, $end) = ('', '');
|
||||
while (my $status = Filter::Util::Call::filter_read()) {
|
||||
return $status if $status < 0;
|
||||
if (/^__(?:END|DATA)__\r?$/) {
|
||||
$end = $_;
|
||||
last;
|
||||
}
|
||||
$data .= $_;
|
||||
$_ = '';
|
||||
}
|
||||
$_ = $data;
|
||||
my @my_subs;
|
||||
s[^(sub\s+\w+\s+\{)(.*\n)]
|
||||
[${1}my \$self = shift;$2]gm;
|
||||
s[^(sub\s+\w+)\s*\(\s*\)(\s+\{.*\n)]
|
||||
[${1}${2}]gm;
|
||||
s[^my\s+sub\s+(\w+)(\s+\{)(.*)((?s:.*?\n))\}\n]
|
||||
[push @my_subs, $1; "\$$1 = sub$2my \$self = shift;$3$4\};\n"]gem;
|
||||
my $preclare = '';
|
||||
if (@my_subs) {
|
||||
$preclare = join ',', map "\$$_", @my_subs;
|
||||
$preclare = "my($preclare);";
|
||||
}
|
||||
$_ = "use strict;use warnings;$preclare${_};1;\n$end";
|
||||
if ($filter_dump) { print; exit }
|
||||
if ($filter_save) { $filter_result = $_; $_ = $filter_result; }
|
||||
$done = 1;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sub base {
|
||||
push @_, -base;
|
||||
goto &import;
|
||||
}
|
||||
|
||||
sub all_my_bases {
|
||||
my $class = shift;
|
||||
|
||||
return $bases_map->{$class}
|
||||
if defined $bases_map->{$class};
|
||||
|
||||
my @bases = ($class);
|
||||
no strict 'refs';
|
||||
for my $base_class (@{"${class}::ISA"}) {
|
||||
push @bases, @{all_my_bases($base_class)};
|
||||
}
|
||||
my $used = {};
|
||||
$bases_map->{$class} = [grep {not $used->{$_}++} @bases];
|
||||
}
|
||||
|
||||
my %code = (
|
||||
sub_start =>
|
||||
"sub {\n",
|
||||
set_default =>
|
||||
" \$_[0]->{%s} = %s\n unless exists \$_[0]->{%s};\n",
|
||||
init =>
|
||||
" return \$_[0]->{%s} = do { my \$self = \$_[0]; %s }\n" .
|
||||
" unless \$#_ > 0 or defined \$_[0]->{%s};\n",
|
||||
weak_init =>
|
||||
" return do {\n" .
|
||||
" \$_[0]->{%s} = do { my \$self = \$_[0]; %s };\n" .
|
||||
" Scalar::Util::weaken(\$_[0]->{%s}) if ref \$_[0]->{%s};\n" .
|
||||
" \$_[0]->{%s};\n" .
|
||||
" } unless \$#_ > 0 or defined \$_[0]->{%s};\n",
|
||||
return_if_get =>
|
||||
" return \$_[0]->{%s} unless \$#_ > 0;\n",
|
||||
set =>
|
||||
" \$_[0]->{%s} = \$_[1];\n",
|
||||
weaken =>
|
||||
" Scalar::Util::weaken(\$_[0]->{%s}) if ref \$_[0]->{%s};\n",
|
||||
sub_end =>
|
||||
" return \$_[0]->{%s};\n}\n",
|
||||
);
|
||||
|
||||
sub field {
|
||||
my $package = caller;
|
||||
my ($args, @values) = do {
|
||||
no warnings;
|
||||
local *boolean_arguments = sub { (qw(-weak)) };
|
||||
local *paired_arguments = sub { (qw(-package -init)) };
|
||||
Spiffy->parse_arguments(@_);
|
||||
};
|
||||
my ($field, $default) = @values;
|
||||
$package = $args->{-package} if defined $args->{-package};
|
||||
die "Cannot have a default for a weakened field ($field)"
|
||||
if defined $default && $args->{-weak};
|
||||
return if defined &{"${package}::$field"};
|
||||
require Scalar::Util if $args->{-weak};
|
||||
my $default_string =
|
||||
( ref($default) eq 'ARRAY' and not @$default )
|
||||
? '[]'
|
||||
: (ref($default) eq 'HASH' and not keys %$default )
|
||||
? '{}'
|
||||
: default_as_code($default);
|
||||
|
||||
my $code = $code{sub_start};
|
||||
if ($args->{-init}) {
|
||||
my $fragment = $args->{-weak} ? $code{weak_init} : $code{init};
|
||||
$code .= sprintf $fragment, $field, $args->{-init}, ($field) x 4;
|
||||
}
|
||||
$code .= sprintf $code{set_default}, $field, $default_string, $field
|
||||
if defined $default;
|
||||
$code .= sprintf $code{return_if_get}, $field;
|
||||
$code .= sprintf $code{set}, $field;
|
||||
$code .= sprintf $code{weaken}, $field, $field
|
||||
if $args->{-weak};
|
||||
$code .= sprintf $code{sub_end}, $field;
|
||||
|
||||
my $sub = eval $code;
|
||||
die $@ if $@;
|
||||
no strict 'refs';
|
||||
*{"${package}::$field"} = $sub;
|
||||
return $code if defined wantarray;
|
||||
}
|
||||
|
||||
sub default_as_code {
|
||||
require Data::Dumper;
|
||||
local $Data::Dumper::Sortkeys = 1;
|
||||
my $code = Data::Dumper::Dumper(shift);
|
||||
$code =~ s/^\$VAR1 = //;
|
||||
$code =~ s/;$//;
|
||||
return $code;
|
||||
}
|
||||
|
||||
sub const {
|
||||
my $package = caller;
|
||||
my ($args, @values) = do {
|
||||
no warnings;
|
||||
local *paired_arguments = sub { (qw(-package)) };
|
||||
Spiffy->parse_arguments(@_);
|
||||
};
|
||||
my ($field, $default) = @values;
|
||||
$package = $args->{-package} if defined $args->{-package};
|
||||
no strict 'refs';
|
||||
return if defined &{"${package}::$field"};
|
||||
*{"${package}::$field"} = sub { $default }
|
||||
}
|
||||
|
||||
sub stub {
|
||||
my $package = caller;
|
||||
my ($args, @values) = do {
|
||||
no warnings;
|
||||
local *paired_arguments = sub { (qw(-package)) };
|
||||
Spiffy->parse_arguments(@_);
|
||||
};
|
||||
my ($field, $default) = @values;
|
||||
$package = $args->{-package} if defined $args->{-package};
|
||||
no strict 'refs';
|
||||
return if defined &{"${package}::$field"};
|
||||
*{"${package}::$field"} =
|
||||
sub {
|
||||
require Carp;
|
||||
Carp::confess
|
||||
"Method $field in package $package must be subclassed";
|
||||
}
|
||||
}
|
||||
|
||||
sub parse_arguments {
|
||||
my $class = shift;
|
||||
my ($args, @values) = ({}, ());
|
||||
my %booleans = map { ($_, 1) } $class->boolean_arguments;
|
||||
my %pairs = map { ($_, 1) } $class->paired_arguments;
|
||||
while (@_) {
|
||||
my $elem = shift;
|
||||
if (defined $elem and defined $booleans{$elem}) {
|
||||
$args->{$elem} = (@_ and $_[0] =~ /^[01]$/)
|
||||
? shift
|
||||
: 1;
|
||||
}
|
||||
elsif (defined $elem and defined $pairs{$elem} and @_) {
|
||||
$args->{$elem} = shift;
|
||||
}
|
||||
else {
|
||||
push @values, $elem;
|
||||
}
|
||||
}
|
||||
return wantarray ? ($args, @values) : $args;
|
||||
}
|
||||
|
||||
sub boolean_arguments { () }
|
||||
sub paired_arguments { () }
|
||||
|
||||
# get a unique id for any node
|
||||
sub id {
|
||||
if (not ref $_[0]) {
|
||||
return 'undef' if not defined $_[0];
|
||||
\$_[0] =~ /\((\w+)\)$/o or die;
|
||||
return "$1-S";
|
||||
}
|
||||
require overload;
|
||||
overload::StrVal($_[0]) =~ /\((\w+)\)$/o or die;
|
||||
return $1;
|
||||
}
|
||||
|
||||
#===============================================================================
|
||||
# It's super, man.
|
||||
#===============================================================================
|
||||
package DB;
|
||||
{
|
||||
no warnings 'redefine';
|
||||
sub super_args {
|
||||
my @dummy = caller(@_ ? $_[0] : 2);
|
||||
return @DB::args;
|
||||
}
|
||||
}
|
||||
|
||||
package Spiffy;
|
||||
sub super {
|
||||
my $method;
|
||||
my $frame = 1;
|
||||
while ($method = (caller($frame++))[3]) {
|
||||
$method =~ s/.*::// and last;
|
||||
}
|
||||
my @args = DB::super_args($frame);
|
||||
@_ = @_ ? ($args[0], @_) : @args;
|
||||
my $class = ref $_[0] ? ref $_[0] : $_[0];
|
||||
my $caller_class = caller;
|
||||
my $seen = 0;
|
||||
my @super_classes = reverse grep {
|
||||
($seen or $seen = ($_ eq $caller_class)) ? 0 : 1;
|
||||
} reverse @{all_my_bases($class)};
|
||||
for my $super_class (@super_classes) {
|
||||
no strict 'refs';
|
||||
next if $super_class eq $class;
|
||||
if (defined &{"${super_class}::$method"}) {
|
||||
${"$super_class\::AUTOLOAD"} = ${"$class\::AUTOLOAD"}
|
||||
if $method eq 'AUTOLOAD';
|
||||
return &{"${super_class}::$method"};
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#===============================================================================
|
||||
# This code deserves a spanking, because it is being very naughty.
|
||||
# It is exchanging base.pm's import() for its own, so that people
|
||||
# can use base.pm with Spiffy modules, without being the wiser.
|
||||
#===============================================================================
|
||||
my $real_base_import;
|
||||
my $real_mixin_import;
|
||||
|
||||
BEGIN {
|
||||
require base unless defined $INC{'base.pm'};
|
||||
$INC{'mixin.pm'} ||= 'Spiffy/mixin.pm';
|
||||
$real_base_import = \&base::import;
|
||||
$real_mixin_import = \&mixin::import;
|
||||
no warnings;
|
||||
*base::import = \&spiffy_base_import;
|
||||
*mixin::import = \&spiffy_mixin_import;
|
||||
}
|
||||
|
||||
# my $i = 0;
|
||||
# while (my $caller = caller($i++)) {
|
||||
# next unless $caller eq 'base' or $caller eq 'mixin';
|
||||
# croak <<END;
|
||||
# Spiffy.pm must be loaded before calling 'use base' or 'use mixin' with a
|
||||
# Spiffy module. See the documentation of Spiffy.pm for details.
|
||||
# END
|
||||
# }
|
||||
|
||||
sub spiffy_base_import {
|
||||
my @base_classes = @_;
|
||||
shift @base_classes;
|
||||
no strict 'refs';
|
||||
goto &$real_base_import
|
||||
unless grep {
|
||||
eval "require $_" unless %{"$_\::"};
|
||||
$_->isa('Spiffy');
|
||||
} @base_classes;
|
||||
my $inheritor = caller(0);
|
||||
for my $base_class (@base_classes) {
|
||||
next if $inheritor->isa($base_class);
|
||||
croak "Can't mix Spiffy and non-Spiffy classes in 'use base'.\n",
|
||||
"See the documentation of Spiffy.pm for details\n "
|
||||
unless $base_class->isa('Spiffy');
|
||||
$stack_frame = 1; # tell import to use different caller
|
||||
import($base_class, '-base');
|
||||
$stack_frame = 0;
|
||||
}
|
||||
}
|
||||
|
||||
sub mixin {
|
||||
my $self = shift;
|
||||
my $target_class = ref($self);
|
||||
spiffy_mixin_import($target_class, @_)
|
||||
}
|
||||
|
||||
sub spiffy_mixin_import {
|
||||
my $target_class = shift;
|
||||
$target_class = caller(0)
|
||||
if $target_class eq 'mixin';
|
||||
my $mixin_class = shift
|
||||
or die "Nothing to mixin";
|
||||
eval "require $mixin_class";
|
||||
my @roles = @_;
|
||||
my $pseudo_class = join '-', $target_class, $mixin_class, @roles;
|
||||
my %methods = spiffy_mixin_methods($mixin_class, @roles);
|
||||
no strict 'refs';
|
||||
no warnings;
|
||||
@{"$pseudo_class\::ISA"} = @{"$target_class\::ISA"};
|
||||
@{"$target_class\::ISA"} = ($pseudo_class);
|
||||
for (keys %methods) {
|
||||
*{"$pseudo_class\::$_"} = $methods{$_};
|
||||
}
|
||||
}
|
||||
|
||||
sub spiffy_mixin_methods {
|
||||
my $mixin_class = shift;
|
||||
no strict 'refs';
|
||||
my %methods = spiffy_all_methods($mixin_class);
|
||||
map {
|
||||
$methods{$_}
|
||||
? ($_, \ &{"$methods{$_}\::$_"})
|
||||
: ($_, \ &{"$mixin_class\::$_"})
|
||||
} @_
|
||||
? (get_roles($mixin_class, @_))
|
||||
: (keys %methods);
|
||||
}
|
||||
|
||||
sub get_roles {
|
||||
my $mixin_class = shift;
|
||||
my @roles = @_;
|
||||
while (grep /^!*:/, @roles) {
|
||||
@roles = map {
|
||||
s/!!//g;
|
||||
/^!:(.*)/ ? do {
|
||||
my $m = "_role_$1";
|
||||
map("!$_", $mixin_class->$m);
|
||||
} :
|
||||
/^:(.*)/ ? do {
|
||||
my $m = "_role_$1";
|
||||
($mixin_class->$m);
|
||||
} :
|
||||
($_)
|
||||
} @roles;
|
||||
}
|
||||
if (@roles and $roles[0] =~ /^!/) {
|
||||
my %methods = spiffy_all_methods($mixin_class);
|
||||
unshift @roles, keys(%methods);
|
||||
}
|
||||
my %roles;
|
||||
for (@roles) {
|
||||
s/!!//g;
|
||||
delete $roles{$1}, next
|
||||
if /^!(.*)/;
|
||||
$roles{$_} = 1;
|
||||
}
|
||||
keys %roles;
|
||||
}
|
||||
|
||||
sub spiffy_all_methods {
|
||||
no strict 'refs';
|
||||
my $class = shift;
|
||||
return if $class eq 'Spiffy';
|
||||
my %methods = map {
|
||||
($_, $class)
|
||||
} grep {
|
||||
defined &{"$class\::$_"} and not /^_/
|
||||
} keys %{"$class\::"};
|
||||
my %super_methods;
|
||||
%super_methods = spiffy_all_methods(${"$class\::ISA"}[0])
|
||||
if @{"$class\::ISA"};
|
||||
%{{%super_methods, %methods}};
|
||||
}
|
||||
|
||||
|
||||
# END of naughty code.
|
||||
#===============================================================================
|
||||
# Debugging support
|
||||
#===============================================================================
|
||||
sub spiffy_dump {
|
||||
no warnings;
|
||||
if ($dump eq 'dumper') {
|
||||
require Data::Dumper;
|
||||
$Data::Dumper::Sortkeys = 1;
|
||||
$Data::Dumper::Indent = 1;
|
||||
return Data::Dumper::Dumper(@_);
|
||||
}
|
||||
require YAML;
|
||||
$YAML::UseVersion = 0;
|
||||
return YAML::Dump(@_) . "...\n";
|
||||
}
|
||||
|
||||
sub at_line_number {
|
||||
my ($file_path, $line_number) = (caller(1))[1,2];
|
||||
" at $file_path line $line_number\n";
|
||||
}
|
||||
|
||||
sub WWW {
|
||||
warn spiffy_dump(@_) . at_line_number;
|
||||
return wantarray ? @_ : $_[0];
|
||||
}
|
||||
|
||||
sub XXX {
|
||||
die spiffy_dump(@_) . at_line_number;
|
||||
}
|
||||
|
||||
sub YYY {
|
||||
print spiffy_dump(@_) . at_line_number;
|
||||
return wantarray ? @_ : $_[0];
|
||||
}
|
||||
|
||||
sub ZZZ {
|
||||
require Carp;
|
||||
Carp::confess spiffy_dump(@_);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
#line 1066
|
@ -0,0 +1,682 @@
|
||||
#line 1
|
||||
package Test::Base;
|
||||
use 5.006001;
|
||||
use Spiffy 0.30 -Base;
|
||||
use Spiffy ':XXX';
|
||||
our $VERSION = '0.60';
|
||||
|
||||
my @test_more_exports;
|
||||
BEGIN {
|
||||
@test_more_exports = qw(
|
||||
ok isnt like unlike is_deeply cmp_ok
|
||||
skip todo_skip pass fail
|
||||
eq_array eq_hash eq_set
|
||||
plan can_ok isa_ok diag
|
||||
use_ok
|
||||
$TODO
|
||||
);
|
||||
}
|
||||
|
||||
use Test::More import => \@test_more_exports;
|
||||
use Carp;
|
||||
|
||||
our @EXPORT = (@test_more_exports, qw(
|
||||
is no_diff
|
||||
|
||||
blocks next_block first_block
|
||||
delimiters spec_file spec_string
|
||||
filters filters_delay filter_arguments
|
||||
run run_compare run_is run_is_deeply run_like run_unlike
|
||||
skip_all_unless_require is_deep run_is_deep
|
||||
WWW XXX YYY ZZZ
|
||||
tie_output no_diag_on_only
|
||||
|
||||
find_my_self default_object
|
||||
|
||||
croak carp cluck confess
|
||||
));
|
||||
|
||||
field '_spec_file';
|
||||
field '_spec_string';
|
||||
field _filters => [qw(norm trim)];
|
||||
field _filters_map => {};
|
||||
field spec =>
|
||||
-init => '$self->_spec_init';
|
||||
field block_list =>
|
||||
-init => '$self->_block_list_init';
|
||||
field _next_list => [];
|
||||
field block_delim =>
|
||||
-init => '$self->block_delim_default';
|
||||
field data_delim =>
|
||||
-init => '$self->data_delim_default';
|
||||
field _filters_delay => 0;
|
||||
field _no_diag_on_only => 0;
|
||||
|
||||
field block_delim_default => '===';
|
||||
field data_delim_default => '---';
|
||||
|
||||
my $default_class;
|
||||
my $default_object;
|
||||
my $reserved_section_names = {};
|
||||
|
||||
sub default_object {
|
||||
$default_object ||= $default_class->new;
|
||||
return $default_object;
|
||||
}
|
||||
|
||||
my $import_called = 0;
|
||||
sub import() {
|
||||
$import_called = 1;
|
||||
my $class = (grep /^-base$/i, @_)
|
||||
? scalar(caller)
|
||||
: $_[0];
|
||||
if (not defined $default_class) {
|
||||
$default_class = $class;
|
||||
}
|
||||
# else {
|
||||
# croak "Can't use $class after using $default_class"
|
||||
# unless $default_class->isa($class);
|
||||
# }
|
||||
|
||||
unless (grep /^-base$/i, @_) {
|
||||
my @args;
|
||||
for (my $ii = 1; $ii <= $#_; ++$ii) {
|
||||
if ($_[$ii] eq '-package') {
|
||||
++$ii;
|
||||
} else {
|
||||
push @args, $_[$ii];
|
||||
}
|
||||
}
|
||||
Test::More->import(import => \@test_more_exports, @args)
|
||||
if @args;
|
||||
}
|
||||
|
||||
_strict_warnings();
|
||||
goto &Spiffy::import;
|
||||
}
|
||||
|
||||
# Wrap Test::Builder::plan
|
||||
my $plan_code = \&Test::Builder::plan;
|
||||
my $Have_Plan = 0;
|
||||
{
|
||||
no warnings 'redefine';
|
||||
*Test::Builder::plan = sub {
|
||||
$Have_Plan = 1;
|
||||
goto &$plan_code;
|
||||
};
|
||||
}
|
||||
|
||||
my $DIED = 0;
|
||||
$SIG{__DIE__} = sub { $DIED = 1; die @_ };
|
||||
|
||||
sub block_class { $self->find_class('Block') }
|
||||
sub filter_class { $self->find_class('Filter') }
|
||||
|
||||
sub find_class {
|
||||
my $suffix = shift;
|
||||
my $class = ref($self) . "::$suffix";
|
||||
return $class if $class->can('new');
|
||||
$class = __PACKAGE__ . "::$suffix";
|
||||
return $class if $class->can('new');
|
||||
eval "require $class";
|
||||
return $class if $class->can('new');
|
||||
die "Can't find a class for $suffix";
|
||||
}
|
||||
|
||||
sub check_late {
|
||||
if ($self->{block_list}) {
|
||||
my $caller = (caller(1))[3];
|
||||
$caller =~ s/.*:://;
|
||||
croak "Too late to call $caller()"
|
||||
}
|
||||
}
|
||||
|
||||
sub find_my_self() {
|
||||
my $self = ref($_[0]) eq $default_class
|
||||
? splice(@_, 0, 1)
|
||||
: default_object();
|
||||
return $self, @_;
|
||||
}
|
||||
|
||||
sub blocks() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
|
||||
croak "Invalid arguments passed to 'blocks'"
|
||||
if @_ > 1;
|
||||
croak sprintf("'%s' is invalid argument to blocks()", shift(@_))
|
||||
if @_ && $_[0] !~ /^[a-zA-Z]\w*$/;
|
||||
|
||||
my $blocks = $self->block_list;
|
||||
|
||||
my $section_name = shift || '';
|
||||
my @blocks = $section_name
|
||||
? (grep { exists $_->{$section_name} } @$blocks)
|
||||
: (@$blocks);
|
||||
|
||||
return scalar(@blocks) unless wantarray;
|
||||
|
||||
return (@blocks) if $self->_filters_delay;
|
||||
|
||||
for my $block (@blocks) {
|
||||
$block->run_filters
|
||||
unless $block->is_filtered;
|
||||
}
|
||||
|
||||
return (@blocks);
|
||||
}
|
||||
|
||||
sub next_block() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
my $list = $self->_next_list;
|
||||
if (@$list == 0) {
|
||||
$list = [@{$self->block_list}, undef];
|
||||
$self->_next_list($list);
|
||||
}
|
||||
my $block = shift @$list;
|
||||
if (defined $block and not $block->is_filtered) {
|
||||
$block->run_filters;
|
||||
}
|
||||
return $block;
|
||||
}
|
||||
|
||||
sub first_block() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
$self->_next_list([]);
|
||||
$self->next_block;
|
||||
}
|
||||
|
||||
sub filters_delay() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
$self->_filters_delay(defined $_[0] ? shift : 1);
|
||||
}
|
||||
|
||||
sub no_diag_on_only() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
$self->_no_diag_on_only(defined $_[0] ? shift : 1);
|
||||
}
|
||||
|
||||
sub delimiters() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
$self->check_late;
|
||||
my ($block_delimiter, $data_delimiter) = @_;
|
||||
$block_delimiter ||= $self->block_delim_default;
|
||||
$data_delimiter ||= $self->data_delim_default;
|
||||
$self->block_delim($block_delimiter);
|
||||
$self->data_delim($data_delimiter);
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub spec_file() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
$self->check_late;
|
||||
$self->_spec_file(shift);
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub spec_string() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
$self->check_late;
|
||||
$self->_spec_string(shift);
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub filters() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
if (ref($_[0]) eq 'HASH') {
|
||||
$self->_filters_map(shift);
|
||||
}
|
||||
else {
|
||||
my $filters = $self->_filters;
|
||||
push @$filters, @_;
|
||||
}
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub filter_arguments() {
|
||||
$Test::Base::Filter::arguments;
|
||||
}
|
||||
|
||||
sub have_text_diff {
|
||||
eval { require Text::Diff; 1 } &&
|
||||
$Text::Diff::VERSION >= 0.35 &&
|
||||
$Algorithm::Diff::VERSION >= 1.15;
|
||||
}
|
||||
|
||||
sub is($$;$) {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
my ($actual, $expected, $name) = @_;
|
||||
local $Test::Builder::Level = $Test::Builder::Level + 1;
|
||||
if ($ENV{TEST_SHOW_NO_DIFFS} or
|
||||
not defined $actual or
|
||||
not defined $expected or
|
||||
$actual eq $expected or
|
||||
not($self->have_text_diff) or
|
||||
$expected !~ /\n./s
|
||||
) {
|
||||
Test::More::is($actual, $expected, $name);
|
||||
}
|
||||
else {
|
||||
$name = '' unless defined $name;
|
||||
ok $actual eq $expected,
|
||||
$name . "\n" . Text::Diff::diff(\$expected, \$actual);
|
||||
}
|
||||
}
|
||||
|
||||
sub run(&;$) {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
my $callback = shift;
|
||||
for my $block (@{$self->block_list}) {
|
||||
$block->run_filters unless $block->is_filtered;
|
||||
&{$callback}($block);
|
||||
}
|
||||
}
|
||||
|
||||
my $name_error = "Can't determine section names";
|
||||
sub _section_names {
|
||||
return @_ if @_ == 2;
|
||||
my $block = $self->first_block
|
||||
or croak $name_error;
|
||||
my @names = grep {
|
||||
$_ !~ /^(ONLY|LAST|SKIP)$/;
|
||||
} @{$block->{_section_order}[0] || []};
|
||||
croak "$name_error. Need two sections in first block"
|
||||
unless @names == 2;
|
||||
return @names;
|
||||
}
|
||||
|
||||
sub _assert_plan {
|
||||
plan('no_plan') unless $Have_Plan;
|
||||
}
|
||||
|
||||
sub END {
|
||||
run_compare() unless $Have_Plan or $DIED or not $import_called;
|
||||
}
|
||||
|
||||
sub run_compare() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
$self->_assert_plan;
|
||||
my ($x, $y) = $self->_section_names(@_);
|
||||
local $Test::Builder::Level = $Test::Builder::Level + 1;
|
||||
for my $block (@{$self->block_list}) {
|
||||
next unless exists($block->{$x}) and exists($block->{$y});
|
||||
$block->run_filters unless $block->is_filtered;
|
||||
if (ref $block->$x) {
|
||||
is_deeply($block->$x, $block->$y,
|
||||
$block->name ? $block->name : ());
|
||||
}
|
||||
elsif (ref $block->$y eq 'Regexp') {
|
||||
my $regexp = ref $y ? $y : $block->$y;
|
||||
like($block->$x, $regexp, $block->name ? $block->name : ());
|
||||
}
|
||||
else {
|
||||
is($block->$x, $block->$y, $block->name ? $block->name : ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub run_is() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
$self->_assert_plan;
|
||||
my ($x, $y) = $self->_section_names(@_);
|
||||
local $Test::Builder::Level = $Test::Builder::Level + 1;
|
||||
for my $block (@{$self->block_list}) {
|
||||
next unless exists($block->{$x}) and exists($block->{$y});
|
||||
$block->run_filters unless $block->is_filtered;
|
||||
is($block->$x, $block->$y,
|
||||
$block->name ? $block->name : ()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sub run_is_deeply() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
$self->_assert_plan;
|
||||
my ($x, $y) = $self->_section_names(@_);
|
||||
for my $block (@{$self->block_list}) {
|
||||
next unless exists($block->{$x}) and exists($block->{$y});
|
||||
$block->run_filters unless $block->is_filtered;
|
||||
is_deeply($block->$x, $block->$y,
|
||||
$block->name ? $block->name : ()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sub run_like() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
$self->_assert_plan;
|
||||
my ($x, $y) = $self->_section_names(@_);
|
||||
for my $block (@{$self->block_list}) {
|
||||
next unless exists($block->{$x}) and defined($y);
|
||||
$block->run_filters unless $block->is_filtered;
|
||||
my $regexp = ref $y ? $y : $block->$y;
|
||||
like($block->$x, $regexp,
|
||||
$block->name ? $block->name : ()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sub run_unlike() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
$self->_assert_plan;
|
||||
my ($x, $y) = $self->_section_names(@_);
|
||||
for my $block (@{$self->block_list}) {
|
||||
next unless exists($block->{$x}) and defined($y);
|
||||
$block->run_filters unless $block->is_filtered;
|
||||
my $regexp = ref $y ? $y : $block->$y;
|
||||
unlike($block->$x, $regexp,
|
||||
$block->name ? $block->name : ()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sub skip_all_unless_require() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
my $module = shift;
|
||||
eval "require $module; 1"
|
||||
or Test::More::plan(
|
||||
skip_all => "$module failed to load"
|
||||
);
|
||||
}
|
||||
|
||||
sub is_deep() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
require Test::Deep;
|
||||
Test::Deep::cmp_deeply(@_);
|
||||
}
|
||||
|
||||
sub run_is_deep() {
|
||||
(my ($self), @_) = find_my_self(@_);
|
||||
$self->_assert_plan;
|
||||
my ($x, $y) = $self->_section_names(@_);
|
||||
for my $block (@{$self->block_list}) {
|
||||
next unless exists($block->{$x}) and exists($block->{$y});
|
||||
$block->run_filters unless $block->is_filtered;
|
||||
is_deep($block->$x, $block->$y,
|
||||
$block->name ? $block->name : ()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sub _pre_eval {
|
||||
my $spec = shift;
|
||||
return $spec unless $spec =~
|
||||
s/\A\s*<<<(.*?)>>>\s*$//sm;
|
||||
my $eval_code = $1;
|
||||
eval "package main; $eval_code";
|
||||
croak $@ if $@;
|
||||
return $spec;
|
||||
}
|
||||
|
||||
sub _block_list_init {
|
||||
my $spec = $self->spec;
|
||||
$spec = $self->_pre_eval($spec);
|
||||
my $cd = $self->block_delim;
|
||||
my @hunks = ($spec =~ /^(\Q${cd}\E.*?(?=^\Q${cd}\E|\z))/msg);
|
||||
my $blocks = $self->_choose_blocks(@hunks);
|
||||
$self->block_list($blocks); # Need to set early for possible filter use
|
||||
my $seq = 1;
|
||||
for my $block (@$blocks) {
|
||||
$block->blocks_object($self);
|
||||
$block->seq_num($seq++);
|
||||
}
|
||||
return $blocks;
|
||||
}
|
||||
|
||||
sub _choose_blocks {
|
||||
my $blocks = [];
|
||||
for my $hunk (@_) {
|
||||
my $block = $self->_make_block($hunk);
|
||||
if (exists $block->{ONLY}) {
|
||||
diag "I found ONLY: maybe you're debugging?"
|
||||
unless $self->_no_diag_on_only;
|
||||
return [$block];
|
||||
}
|
||||
next if exists $block->{SKIP};
|
||||
push @$blocks, $block;
|
||||
if (exists $block->{LAST}) {
|
||||
return $blocks;
|
||||
}
|
||||
}
|
||||
return $blocks;
|
||||
}
|
||||
|
||||
sub _check_reserved {
|
||||
my $id = shift;
|
||||
croak "'$id' is a reserved name. Use something else.\n"
|
||||
if $reserved_section_names->{$id} or
|
||||
$id =~ /^_/;
|
||||
}
|
||||
|
||||
sub _make_block {
|
||||
my $hunk = shift;
|
||||
my $cd = $self->block_delim;
|
||||
my $dd = $self->data_delim;
|
||||
my $block = $self->block_class->new;
|
||||
$hunk =~ s/\A\Q${cd}\E[ \t]*(.*)\s+// or die;
|
||||
my $name = $1;
|
||||
my @parts = split /^\Q${dd}\E +\(?(\w+)\)? *(.*)?\n/m, $hunk;
|
||||
my $description = shift @parts;
|
||||
$description ||= '';
|
||||
unless ($description =~ /\S/) {
|
||||
$description = $name;
|
||||
}
|
||||
$description =~ s/\s*\z//;
|
||||
$block->set_value(description => $description);
|
||||
|
||||
my $section_map = {};
|
||||
my $section_order = [];
|
||||
while (@parts) {
|
||||
my ($type, $filters, $value) = splice(@parts, 0, 3);
|
||||
$self->_check_reserved($type);
|
||||
$value = '' unless defined $value;
|
||||
$filters = '' unless defined $filters;
|
||||
if ($filters =~ /:(\s|\z)/) {
|
||||
croak "Extra lines not allowed in '$type' section"
|
||||
if $value =~ /\S/;
|
||||
($filters, $value) = split /\s*:(?:\s+|\z)/, $filters, 2;
|
||||
$value = '' unless defined $value;
|
||||
$value =~ s/^\s*(.*?)\s*$/$1/;
|
||||
}
|
||||
$section_map->{$type} = {
|
||||
filters => $filters,
|
||||
};
|
||||
push @$section_order, $type;
|
||||
$block->set_value($type, $value);
|
||||
}
|
||||
$block->set_value(name => $name);
|
||||
$block->set_value(_section_map => $section_map);
|
||||
$block->set_value(_section_order => $section_order);
|
||||
return $block;
|
||||
}
|
||||
|
||||
sub _spec_init {
|
||||
return $self->_spec_string
|
||||
if $self->_spec_string;
|
||||
local $/;
|
||||
my $spec;
|
||||
if (my $spec_file = $self->_spec_file) {
|
||||
open FILE, $spec_file or die $!;
|
||||
$spec = <FILE>;
|
||||
close FILE;
|
||||
}
|
||||
else {
|
||||
$spec = do {
|
||||
package main;
|
||||
no warnings 'once';
|
||||
<DATA>;
|
||||
};
|
||||
}
|
||||
return $spec;
|
||||
}
|
||||
|
||||
sub _strict_warnings() {
|
||||
require Filter::Util::Call;
|
||||
my $done = 0;
|
||||
Filter::Util::Call::filter_add(
|
||||
sub {
|
||||
return 0 if $done;
|
||||
my ($data, $end) = ('', '');
|
||||
while (my $status = Filter::Util::Call::filter_read()) {
|
||||
return $status if $status < 0;
|
||||
if (/^__(?:END|DATA)__\r?$/) {
|
||||
$end = $_;
|
||||
last;
|
||||
}
|
||||
$data .= $_;
|
||||
$_ = '';
|
||||
}
|
||||
$_ = "use strict;use warnings;$data$end";
|
||||
$done = 1;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sub tie_output() {
|
||||
my $handle = shift;
|
||||
die "No buffer to tie" unless @_;
|
||||
tie *$handle, 'Test::Base::Handle', $_[0];
|
||||
}
|
||||
|
||||
sub no_diff {
|
||||
$ENV{TEST_SHOW_NO_DIFFS} = 1;
|
||||
}
|
||||
|
||||
package Test::Base::Handle;
|
||||
|
||||
sub TIEHANDLE() {
|
||||
my $class = shift;
|
||||
bless \ $_[0], $class;
|
||||
}
|
||||
|
||||
sub PRINT {
|
||||
$$self .= $_ for @_;
|
||||
}
|
||||
|
||||
#===============================================================================
|
||||
# Test::Base::Block
|
||||
#
|
||||
# This is the default class for accessing a Test::Base block object.
|
||||
#===============================================================================
|
||||
package Test::Base::Block;
|
||||
our @ISA = qw(Spiffy);
|
||||
|
||||
our @EXPORT = qw(block_accessor);
|
||||
|
||||
sub AUTOLOAD {
|
||||
return;
|
||||
}
|
||||
|
||||
sub block_accessor() {
|
||||
my $accessor = shift;
|
||||
no strict 'refs';
|
||||
return if defined &$accessor;
|
||||
*$accessor = sub {
|
||||
my $self = shift;
|
||||
if (@_) {
|
||||
Carp::croak "Not allowed to set values for '$accessor'";
|
||||
}
|
||||
my @list = @{$self->{$accessor} || []};
|
||||
return wantarray
|
||||
? (@list)
|
||||
: $list[0];
|
||||
};
|
||||
}
|
||||
|
||||
block_accessor 'name';
|
||||
block_accessor 'description';
|
||||
Spiffy::field 'seq_num';
|
||||
Spiffy::field 'is_filtered';
|
||||
Spiffy::field 'blocks_object';
|
||||
Spiffy::field 'original_values' => {};
|
||||
|
||||
sub set_value {
|
||||
no strict 'refs';
|
||||
my $accessor = shift;
|
||||
block_accessor $accessor
|
||||
unless defined &$accessor;
|
||||
$self->{$accessor} = [@_];
|
||||
}
|
||||
|
||||
sub run_filters {
|
||||
my $map = $self->_section_map;
|
||||
my $order = $self->_section_order;
|
||||
Carp::croak "Attempt to filter a block twice"
|
||||
if $self->is_filtered;
|
||||
for my $type (@$order) {
|
||||
my $filters = $map->{$type}{filters};
|
||||
my @value = $self->$type;
|
||||
$self->original_values->{$type} = $value[0];
|
||||
for my $filter ($self->_get_filters($type, $filters)) {
|
||||
$Test::Base::Filter::arguments =
|
||||
$filter =~ s/=(.*)$// ? $1 : undef;
|
||||
my $function = "main::$filter";
|
||||
no strict 'refs';
|
||||
if (defined &$function) {
|
||||
local $_ =
|
||||
(@value == 1 and not defined($value[0])) ? undef :
|
||||
join '', @value;
|
||||
my $old = $_;
|
||||
@value = &$function(@value);
|
||||
if (not(@value) or
|
||||
@value == 1 and defined($value[0]) and $value[0] =~ /\A(\d+|)\z/
|
||||
) {
|
||||
if ($value[0] && $_ eq $old) {
|
||||
Test::Base::diag("Filters returning numbers are supposed to do munging \$_: your filter '$function' apparently doesn't.");
|
||||
}
|
||||
@value = ($_);
|
||||
}
|
||||
}
|
||||
else {
|
||||
my $filter_object = $self->blocks_object->filter_class->new;
|
||||
die "Can't find a function or method for '$filter' filter\n"
|
||||
unless $filter_object->can($filter);
|
||||
$filter_object->current_block($self);
|
||||
@value = $filter_object->$filter(@value);
|
||||
}
|
||||
# Set the value after each filter since other filters may be
|
||||
# introspecting.
|
||||
$self->set_value($type, @value);
|
||||
}
|
||||
}
|
||||
$self->is_filtered(1);
|
||||
}
|
||||
|
||||
sub _get_filters {
|
||||
my $type = shift;
|
||||
my $string = shift || '';
|
||||
$string =~ s/\s*(.*?)\s*/$1/;
|
||||
my @filters = ();
|
||||
my $map_filters = $self->blocks_object->_filters_map->{$type} || [];
|
||||
$map_filters = [ $map_filters ] unless ref $map_filters;
|
||||
my @append = ();
|
||||
for (
|
||||
@{$self->blocks_object->_filters},
|
||||
@$map_filters,
|
||||
split(/\s+/, $string),
|
||||
) {
|
||||
my $filter = $_;
|
||||
last unless length $filter;
|
||||
if ($filter =~ s/^-//) {
|
||||
@filters = grep { $_ ne $filter } @filters;
|
||||
}
|
||||
elsif ($filter =~ s/^\+//) {
|
||||
push @append, $filter;
|
||||
}
|
||||
else {
|
||||
push @filters, $filter;
|
||||
}
|
||||
}
|
||||
return @filters, @append;
|
||||
}
|
||||
|
||||
{
|
||||
%$reserved_section_names = map {
|
||||
($_, 1);
|
||||
} keys(%Test::Base::Block::), qw( new DESTROY );
|
||||
}
|
||||
|
||||
__DATA__
|
||||
|
||||
=encoding utf8
|
||||
|
||||
#line 1374
|
@ -0,0 +1,341 @@
|
||||
#line 1
|
||||
#===============================================================================
|
||||
# This is the default class for handling Test::Base data filtering.
|
||||
#===============================================================================
|
||||
package Test::Base::Filter;
|
||||
use Spiffy -Base;
|
||||
use Spiffy ':XXX';
|
||||
|
||||
field 'current_block';
|
||||
|
||||
our $arguments;
|
||||
sub current_arguments {
|
||||
return undef unless defined $arguments;
|
||||
my $args = $arguments;
|
||||
$args =~ s/(\\s)/ /g;
|
||||
$args =~ s/(\\[a-z])/'"' . $1 . '"'/gee;
|
||||
return $args;
|
||||
}
|
||||
|
||||
sub assert_scalar {
|
||||
return if @_ == 1;
|
||||
require Carp;
|
||||
my $filter = (caller(1))[3];
|
||||
$filter =~ s/.*:://;
|
||||
Carp::croak "Input to the '$filter' filter must be a scalar, not a list";
|
||||
}
|
||||
|
||||
sub _apply_deepest {
|
||||
my $method = shift;
|
||||
return () unless @_;
|
||||
if (ref $_[0] eq 'ARRAY') {
|
||||
for my $aref (@_) {
|
||||
@$aref = $self->_apply_deepest($method, @$aref);
|
||||
}
|
||||
return @_;
|
||||
}
|
||||
$self->$method(@_);
|
||||
}
|
||||
|
||||
sub _split_array {
|
||||
map {
|
||||
[$self->split($_)];
|
||||
} @_;
|
||||
}
|
||||
|
||||
sub _peel_deepest {
|
||||
return () unless @_;
|
||||
if (ref $_[0] eq 'ARRAY') {
|
||||
if (ref $_[0]->[0] eq 'ARRAY') {
|
||||
for my $aref (@_) {
|
||||
@$aref = $self->_peel_deepest(@$aref);
|
||||
}
|
||||
return @_;
|
||||
}
|
||||
return map { $_->[0] } @_;
|
||||
}
|
||||
return @_;
|
||||
}
|
||||
|
||||
#===============================================================================
|
||||
# these filters work on the leaves of nested arrays
|
||||
#===============================================================================
|
||||
sub Join { $self->_peel_deepest($self->_apply_deepest(join => @_)) }
|
||||
sub Reverse { $self->_apply_deepest(reverse => @_) }
|
||||
sub Split { $self->_apply_deepest(_split_array => @_) }
|
||||
sub Sort { $self->_apply_deepest(sort => @_) }
|
||||
|
||||
|
||||
sub append {
|
||||
my $suffix = $self->current_arguments;
|
||||
map { $_ . $suffix } @_;
|
||||
}
|
||||
|
||||
sub array {
|
||||
return [@_];
|
||||
}
|
||||
|
||||
sub base64_decode {
|
||||
$self->assert_scalar(@_);
|
||||
require MIME::Base64;
|
||||
MIME::Base64::decode_base64(shift);
|
||||
}
|
||||
|
||||
sub base64_encode {
|
||||
$self->assert_scalar(@_);
|
||||
require MIME::Base64;
|
||||
MIME::Base64::encode_base64(shift);
|
||||
}
|
||||
|
||||
sub chomp {
|
||||
map { CORE::chomp; $_ } @_;
|
||||
}
|
||||
|
||||
sub chop {
|
||||
map { CORE::chop; $_ } @_;
|
||||
}
|
||||
|
||||
sub dumper {
|
||||
no warnings 'once';
|
||||
require Data::Dumper;
|
||||
local $Data::Dumper::Sortkeys = 1;
|
||||
local $Data::Dumper::Indent = 1;
|
||||
local $Data::Dumper::Terse = 1;
|
||||
Data::Dumper::Dumper(@_);
|
||||
}
|
||||
|
||||
sub escape {
|
||||
$self->assert_scalar(@_);
|
||||
my $text = shift;
|
||||
$text =~ s/(\\.)/eval "qq{$1}"/ge;
|
||||
return $text;
|
||||
}
|
||||
|
||||
sub eval {
|
||||
$self->assert_scalar(@_);
|
||||
my @return = CORE::eval(shift);
|
||||
return $@ if $@;
|
||||
return @return;
|
||||
}
|
||||
|
||||
sub eval_all {
|
||||
$self->assert_scalar(@_);
|
||||
my $out = '';
|
||||
my $err = '';
|
||||
Test::Base::tie_output(*STDOUT, $out);
|
||||
Test::Base::tie_output(*STDERR, $err);
|
||||
my $return = CORE::eval(shift);
|
||||
no warnings;
|
||||
untie *STDOUT;
|
||||
untie *STDERR;
|
||||
return $return, $@, $out, $err;
|
||||
}
|
||||
|
||||
sub eval_stderr {
|
||||
$self->assert_scalar(@_);
|
||||
my $output = '';
|
||||
Test::Base::tie_output(*STDERR, $output);
|
||||
CORE::eval(shift);
|
||||
no warnings;
|
||||
untie *STDERR;
|
||||
return $output;
|
||||
}
|
||||
|
||||
sub eval_stdout {
|
||||
$self->assert_scalar(@_);
|
||||
my $output = '';
|
||||
Test::Base::tie_output(*STDOUT, $output);
|
||||
CORE::eval(shift);
|
||||
no warnings;
|
||||
untie *STDOUT;
|
||||
return $output;
|
||||
}
|
||||
|
||||
sub exec_perl_stdout {
|
||||
my $tmpfile = "/tmp/test-blocks-$$";
|
||||
$self->_write_to($tmpfile, @_);
|
||||
open my $execution, "$^X $tmpfile 2>&1 |"
|
||||
or die "Couldn't open subprocess: $!\n";
|
||||
local $/;
|
||||
my $output = <$execution>;
|
||||
close $execution;
|
||||
unlink($tmpfile)
|
||||
or die "Couldn't unlink $tmpfile: $!\n";
|
||||
return $output;
|
||||
}
|
||||
|
||||
sub flatten {
|
||||
$self->assert_scalar(@_);
|
||||
my $ref = shift;
|
||||
if (ref($ref) eq 'HASH') {
|
||||
return map {
|
||||
($_, $ref->{$_});
|
||||
} sort keys %$ref;
|
||||
}
|
||||
if (ref($ref) eq 'ARRAY') {
|
||||
return @$ref;
|
||||
}
|
||||
die "Can only flatten a hash or array ref";
|
||||
}
|
||||
|
||||
sub get_url {
|
||||
$self->assert_scalar(@_);
|
||||
my $url = shift;
|
||||
CORE::chomp($url);
|
||||
require LWP::Simple;
|
||||
LWP::Simple::get($url);
|
||||
}
|
||||
|
||||
sub hash {
|
||||
return +{ @_ };
|
||||
}
|
||||
|
||||
sub head {
|
||||
my $size = $self->current_arguments || 1;
|
||||
return splice(@_, 0, $size);
|
||||
}
|
||||
|
||||
sub join {
|
||||
my $string = $self->current_arguments;
|
||||
$string = '' unless defined $string;
|
||||
CORE::join $string, @_;
|
||||
}
|
||||
|
||||
sub lines {
|
||||
$self->assert_scalar(@_);
|
||||
my $text = shift;
|
||||
return () unless length $text;
|
||||
my @lines = ($text =~ /^(.*\n?)/gm);
|
||||
return @lines;
|
||||
}
|
||||
|
||||
sub norm {
|
||||
$self->assert_scalar(@_);
|
||||
my $text = shift;
|
||||
$text = '' unless defined $text;
|
||||
$text =~ s/\015\012/\n/g;
|
||||
$text =~ s/\r/\n/g;
|
||||
return $text;
|
||||
}
|
||||
|
||||
sub prepend {
|
||||
my $prefix = $self->current_arguments;
|
||||
map { $prefix . $_ } @_;
|
||||
}
|
||||
|
||||
sub read_file {
|
||||
$self->assert_scalar(@_);
|
||||
my $file = shift;
|
||||
CORE::chomp $file;
|
||||
open my $fh, $file
|
||||
or die "Can't open '$file' for input:\n$!";
|
||||
CORE::join '', <$fh>;
|
||||
}
|
||||
|
||||
sub regexp {
|
||||
$self->assert_scalar(@_);
|
||||
my $text = shift;
|
||||
my $flags = $self->current_arguments;
|
||||
if ($text =~ /\n.*?\n/s) {
|
||||
$flags = 'xism'
|
||||
unless defined $flags;
|
||||
}
|
||||
else {
|
||||
CORE::chomp($text);
|
||||
}
|
||||
$flags ||= '';
|
||||
my $regexp = eval "qr{$text}$flags";
|
||||
die $@ if $@;
|
||||
return $regexp;
|
||||
}
|
||||
|
||||
sub reverse {
|
||||
CORE::reverse(@_);
|
||||
}
|
||||
|
||||
sub slice {
|
||||
die "Invalid args for slice"
|
||||
unless $self->current_arguments =~ /^(\d+)(?:,(\d))?$/;
|
||||
my ($x, $y) = ($1, $2);
|
||||
$y = $x if not defined $y;
|
||||
die "Invalid args for slice"
|
||||
if $x > $y;
|
||||
return splice(@_, $x, 1 + $y - $x);
|
||||
}
|
||||
|
||||
sub sort {
|
||||
CORE::sort(@_);
|
||||
}
|
||||
|
||||
sub split {
|
||||
$self->assert_scalar(@_);
|
||||
my $separator = $self->current_arguments;
|
||||
if (defined $separator and $separator =~ s{^/(.*)/$}{$1}) {
|
||||
my $regexp = $1;
|
||||
$separator = qr{$regexp};
|
||||
}
|
||||
$separator = qr/\s+/ unless $separator;
|
||||
CORE::split $separator, shift;
|
||||
}
|
||||
|
||||
sub strict {
|
||||
$self->assert_scalar(@_);
|
||||
<<'...' . shift;
|
||||
use strict;
|
||||
use warnings;
|
||||
...
|
||||
}
|
||||
|
||||
sub tail {
|
||||
my $size = $self->current_arguments || 1;
|
||||
return splice(@_, @_ - $size, $size);
|
||||
}
|
||||
|
||||
sub trim {
|
||||
map {
|
||||
s/\A([ \t]*\n)+//;
|
||||
s/(?<=\n)\s*\z//g;
|
||||
$_;
|
||||
} @_;
|
||||
}
|
||||
|
||||
sub unchomp {
|
||||
map { $_ . "\n" } @_;
|
||||
}
|
||||
|
||||
sub write_file {
|
||||
my $file = $self->current_arguments
|
||||
or die "No file specified for write_file filter";
|
||||
if ($file =~ /(.*)[\\\/]/) {
|
||||
my $dir = $1;
|
||||
if (not -e $dir) {
|
||||
require File::Path;
|
||||
File::Path::mkpath($dir)
|
||||
or die "Can't create $dir";
|
||||
}
|
||||
}
|
||||
open my $fh, ">$file"
|
||||
or die "Can't open '$file' for output\n:$!";
|
||||
print $fh @_;
|
||||
close $fh;
|
||||
return $file;
|
||||
}
|
||||
|
||||
sub yaml {
|
||||
$self->assert_scalar(@_);
|
||||
require YAML;
|
||||
return YAML::Load(shift);
|
||||
}
|
||||
|
||||
sub _write_to {
|
||||
my $filename = shift;
|
||||
open my $script, ">$filename"
|
||||
or die "Couldn't open $filename: $!\n";
|
||||
print $script @_;
|
||||
close $script
|
||||
or die "Couldn't close $filename: $!\n";
|
||||
}
|
||||
|
||||
__DATA__
|
||||
|
||||
#line 636
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,81 @@
|
||||
#line 1
|
||||
package Test::Builder::Module;
|
||||
|
||||
use strict;
|
||||
|
||||
use Test::Builder;
|
||||
|
||||
require Exporter;
|
||||
our @ISA = qw(Exporter);
|
||||
|
||||
our $VERSION = '0.92';
|
||||
$VERSION = eval $VERSION; ## no critic (BuiltinFunctions::ProhibitStringyEval)
|
||||
|
||||
# 5.004's Exporter doesn't have export_to_level.
|
||||
my $_export_to_level = sub {
|
||||
my $pkg = shift;
|
||||
my $level = shift;
|
||||
(undef) = shift; # redundant arg
|
||||
my $callpkg = caller($level);
|
||||
$pkg->export( $callpkg, @_ );
|
||||
};
|
||||
|
||||
#line 82
|
||||
|
||||
sub import {
|
||||
my($class) = shift;
|
||||
|
||||
# Don't run all this when loading ourself.
|
||||
return 1 if $class eq 'Test::Builder::Module';
|
||||
|
||||
my $test = $class->builder;
|
||||
|
||||
my $caller = caller;
|
||||
|
||||
$test->exported_to($caller);
|
||||
|
||||
$class->import_extra( \@_ );
|
||||
my(@imports) = $class->_strip_imports( \@_ );
|
||||
|
||||
$test->plan(@_);
|
||||
|
||||
$class->$_export_to_level( 1, $class, @imports );
|
||||
}
|
||||
|
||||
sub _strip_imports {
|
||||
my $class = shift;
|
||||
my $list = shift;
|
||||
|
||||
my @imports = ();
|
||||
my @other = ();
|
||||
my $idx = 0;
|
||||
while( $idx <= $#{$list} ) {
|
||||
my $item = $list->[$idx];
|
||||
|
||||
if( defined $item and $item eq 'import' ) {
|
||||
push @imports, @{ $list->[ $idx + 1 ] };
|
||||
$idx++;
|
||||
}
|
||||
else {
|
||||
push @other, $item;
|
||||
}
|
||||
|
||||
$idx++;
|
||||
}
|
||||
|
||||
@$list = @other;
|
||||
|
||||
return @imports;
|
||||
}
|
||||
|
||||
#line 145
|
||||
|
||||
sub import_extra { }
|
||||
|
||||
#line 175
|
||||
|
||||
sub builder {
|
||||
return Test::Builder->new;
|
||||
}
|
||||
|
||||
1;
|
@ -0,0 +1,735 @@
|
||||
#line 1
|
||||
package Test::More;
|
||||
|
||||
use 5.006;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
#---- perlcritic exemptions. ----#
|
||||
|
||||
# We use a lot of subroutine prototypes
|
||||
## no critic (Subroutines::ProhibitSubroutinePrototypes)
|
||||
|
||||
# Can't use Carp because it might cause use_ok() to accidentally succeed
|
||||
# even though the module being used forgot to use Carp. Yes, this
|
||||
# actually happened.
|
||||
sub _carp {
|
||||
my( $file, $line ) = ( caller(1) )[ 1, 2 ];
|
||||
return warn @_, " at $file line $line\n";
|
||||
}
|
||||
|
||||
our $VERSION = '0.92';
|
||||
$VERSION = eval $VERSION; ## no critic (BuiltinFunctions::ProhibitStringyEval)
|
||||
|
||||
use Test::Builder::Module;
|
||||
our @ISA = qw(Test::Builder::Module);
|
||||
our @EXPORT = qw(ok use_ok require_ok
|
||||
is isnt like unlike is_deeply
|
||||
cmp_ok
|
||||
skip todo todo_skip
|
||||
pass fail
|
||||
eq_array eq_hash eq_set
|
||||
$TODO
|
||||
plan
|
||||
done_testing
|
||||
can_ok isa_ok new_ok
|
||||
diag note explain
|
||||
BAIL_OUT
|
||||
);
|
||||
|
||||
#line 163
|
||||
|
||||
sub plan {
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
return $tb->plan(@_);
|
||||
}
|
||||
|
||||
# This implements "use Test::More 'no_diag'" but the behavior is
|
||||
# deprecated.
|
||||
sub import_extra {
|
||||
my $class = shift;
|
||||
my $list = shift;
|
||||
|
||||
my @other = ();
|
||||
my $idx = 0;
|
||||
while( $idx <= $#{$list} ) {
|
||||
my $item = $list->[$idx];
|
||||
|
||||
if( defined $item and $item eq 'no_diag' ) {
|
||||
$class->builder->no_diag(1);
|
||||
}
|
||||
else {
|
||||
push @other, $item;
|
||||
}
|
||||
|
||||
$idx++;
|
||||
}
|
||||
|
||||
@$list = @other;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#line 216
|
||||
|
||||
sub done_testing {
|
||||
my $tb = Test::More->builder;
|
||||
$tb->done_testing(@_);
|
||||
}
|
||||
|
||||
#line 289
|
||||
|
||||
sub ok ($;$) {
|
||||
my( $test, $name ) = @_;
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
return $tb->ok( $test, $name );
|
||||
}
|
||||
|
||||
#line 367
|
||||
|
||||
sub is ($$;$) {
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
return $tb->is_eq(@_);
|
||||
}
|
||||
|
||||
sub isnt ($$;$) {
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
return $tb->isnt_eq(@_);
|
||||
}
|
||||
|
||||
*isn't = \&isnt;
|
||||
|
||||
#line 411
|
||||
|
||||
sub like ($$;$) {
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
return $tb->like(@_);
|
||||
}
|
||||
|
||||
#line 426
|
||||
|
||||
sub unlike ($$;$) {
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
return $tb->unlike(@_);
|
||||
}
|
||||
|
||||
#line 471
|
||||
|
||||
sub cmp_ok($$$;$) {
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
return $tb->cmp_ok(@_);
|
||||
}
|
||||
|
||||
#line 506
|
||||
|
||||
sub can_ok ($@) {
|
||||
my( $proto, @methods ) = @_;
|
||||
my $class = ref $proto || $proto;
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
unless($class) {
|
||||
my $ok = $tb->ok( 0, "->can(...)" );
|
||||
$tb->diag(' can_ok() called with empty class or reference');
|
||||
return $ok;
|
||||
}
|
||||
|
||||
unless(@methods) {
|
||||
my $ok = $tb->ok( 0, "$class->can(...)" );
|
||||
$tb->diag(' can_ok() called with no methods');
|
||||
return $ok;
|
||||
}
|
||||
|
||||
my @nok = ();
|
||||
foreach my $method (@methods) {
|
||||
$tb->_try( sub { $proto->can($method) } ) or push @nok, $method;
|
||||
}
|
||||
|
||||
my $name = (@methods == 1) ? "$class->can('$methods[0]')" :
|
||||
"$class->can(...)" ;
|
||||
|
||||
my $ok = $tb->ok( !@nok, $name );
|
||||
|
||||
$tb->diag( map " $class->can('$_') failed\n", @nok );
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
#line 572
|
||||
|
||||
sub isa_ok ($$;$) {
|
||||
my( $object, $class, $obj_name ) = @_;
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
my $diag;
|
||||
|
||||
if( !defined $object ) {
|
||||
$obj_name = 'The thing' unless defined $obj_name;
|
||||
$diag = "$obj_name isn't defined";
|
||||
}
|
||||
else {
|
||||
my $whatami = ref $object ? 'object' : 'class';
|
||||
# We can't use UNIVERSAL::isa because we want to honor isa() overrides
|
||||
my( $rslt, $error ) = $tb->_try( sub { $object->isa($class) } );
|
||||
if($error) {
|
||||
if( $error =~ /^Can't call method "isa" on unblessed reference/ ) {
|
||||
# Its an unblessed reference
|
||||
$obj_name = 'The reference' unless defined $obj_name;
|
||||
if( !UNIVERSAL::isa( $object, $class ) ) {
|
||||
my $ref = ref $object;
|
||||
$diag = "$obj_name isn't a '$class' it's a '$ref'";
|
||||
}
|
||||
}
|
||||
elsif( $error =~ /Can't call method "isa" without a package/ ) {
|
||||
# It's something that can't even be a class
|
||||
$diag = "$obj_name isn't a class or reference";
|
||||
}
|
||||
else {
|
||||
die <<WHOA;
|
||||
WHOA! I tried to call ->isa on your $whatami and got some weird error.
|
||||
Here's the error.
|
||||
$error
|
||||
WHOA
|
||||
}
|
||||
}
|
||||
else {
|
||||
$obj_name = "The $whatami" unless defined $obj_name;
|
||||
if( !$rslt ) {
|
||||
my $ref = ref $object;
|
||||
$diag = "$obj_name isn't a '$class' it's a '$ref'";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $name = "$obj_name isa $class";
|
||||
my $ok;
|
||||
if($diag) {
|
||||
$ok = $tb->ok( 0, $name );
|
||||
$tb->diag(" $diag\n");
|
||||
}
|
||||
else {
|
||||
$ok = $tb->ok( 1, $name );
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
#line 650
|
||||
|
||||
sub new_ok {
|
||||
my $tb = Test::More->builder;
|
||||
$tb->croak("new_ok() must be given at least a class") unless @_;
|
||||
|
||||
my( $class, $args, $object_name ) = @_;
|
||||
|
||||
$args ||= [];
|
||||
$object_name = "The object" unless defined $object_name;
|
||||
|
||||
my $obj;
|
||||
my( $success, $error ) = $tb->_try( sub { $obj = $class->new(@$args); 1 } );
|
||||
if($success) {
|
||||
local $Test::Builder::Level = $Test::Builder::Level + 1;
|
||||
isa_ok $obj, $class, $object_name;
|
||||
}
|
||||
else {
|
||||
$tb->ok( 0, "new() died" );
|
||||
$tb->diag(" Error was: $error");
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
#line 690
|
||||
|
||||
sub pass (;$) {
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
return $tb->ok( 1, @_ );
|
||||
}
|
||||
|
||||
sub fail (;$) {
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
return $tb->ok( 0, @_ );
|
||||
}
|
||||
|
||||
#line 753
|
||||
|
||||
sub use_ok ($;@) {
|
||||
my( $module, @imports ) = @_;
|
||||
@imports = () unless @imports;
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
my( $pack, $filename, $line ) = caller;
|
||||
|
||||
my $code;
|
||||
if( @imports == 1 and $imports[0] =~ /^\d+(?:\.\d+)?$/ ) {
|
||||
# probably a version check. Perl needs to see the bare number
|
||||
# for it to work with non-Exporter based modules.
|
||||
$code = <<USE;
|
||||
package $pack;
|
||||
use $module $imports[0];
|
||||
1;
|
||||
USE
|
||||
}
|
||||
else {
|
||||
$code = <<USE;
|
||||
package $pack;
|
||||
use $module \@{\$args[0]};
|
||||
1;
|
||||
USE
|
||||
}
|
||||
|
||||
my( $eval_result, $eval_error ) = _eval( $code, \@imports );
|
||||
my $ok = $tb->ok( $eval_result, "use $module;" );
|
||||
|
||||
unless($ok) {
|
||||
chomp $eval_error;
|
||||
$@ =~ s{^BEGIN failed--compilation aborted at .*$}
|
||||
{BEGIN failed--compilation aborted at $filename line $line.}m;
|
||||
$tb->diag(<<DIAGNOSTIC);
|
||||
Tried to use '$module'.
|
||||
Error: $eval_error
|
||||
DIAGNOSTIC
|
||||
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
sub _eval {
|
||||
my( $code, @args ) = @_;
|
||||
|
||||
# Work around oddities surrounding resetting of $@ by immediately
|
||||
# storing it.
|
||||
my( $sigdie, $eval_result, $eval_error );
|
||||
{
|
||||
local( $@, $!, $SIG{__DIE__} ); # isolate eval
|
||||
$eval_result = eval $code; ## no critic (BuiltinFunctions::ProhibitStringyEval)
|
||||
$eval_error = $@;
|
||||
$sigdie = $SIG{__DIE__} || undef;
|
||||
}
|
||||
# make sure that $code got a chance to set $SIG{__DIE__}
|
||||
$SIG{__DIE__} = $sigdie if defined $sigdie;
|
||||
|
||||
return( $eval_result, $eval_error );
|
||||
}
|
||||
|
||||
#line 822
|
||||
|
||||
sub require_ok ($) {
|
||||
my($module) = shift;
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
my $pack = caller;
|
||||
|
||||
# Try to deterine if we've been given a module name or file.
|
||||
# Module names must be barewords, files not.
|
||||
$module = qq['$module'] unless _is_module_name($module);
|
||||
|
||||
my $code = <<REQUIRE;
|
||||
package $pack;
|
||||
require $module;
|
||||
1;
|
||||
REQUIRE
|
||||
|
||||
my( $eval_result, $eval_error ) = _eval($code);
|
||||
my $ok = $tb->ok( $eval_result, "require $module;" );
|
||||
|
||||
unless($ok) {
|
||||
chomp $eval_error;
|
||||
$tb->diag(<<DIAGNOSTIC);
|
||||
Tried to require '$module'.
|
||||
Error: $eval_error
|
||||
DIAGNOSTIC
|
||||
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
sub _is_module_name {
|
||||
my $module = shift;
|
||||
|
||||
# Module names start with a letter.
|
||||
# End with an alphanumeric.
|
||||
# The rest is an alphanumeric or ::
|
||||
$module =~ s/\b::\b//g;
|
||||
|
||||
return $module =~ /^[a-zA-Z]\w*$/ ? 1 : 0;
|
||||
}
|
||||
|
||||
#line 899
|
||||
|
||||
our( @Data_Stack, %Refs_Seen );
|
||||
my $DNE = bless [], 'Does::Not::Exist';
|
||||
|
||||
sub _dne {
|
||||
return ref $_[0] eq ref $DNE;
|
||||
}
|
||||
|
||||
## no critic (Subroutines::RequireArgUnpacking)
|
||||
sub is_deeply {
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
unless( @_ == 2 or @_ == 3 ) {
|
||||
my $msg = <<'WARNING';
|
||||
is_deeply() takes two or three args, you gave %d.
|
||||
This usually means you passed an array or hash instead
|
||||
of a reference to it
|
||||
WARNING
|
||||
chop $msg; # clip off newline so carp() will put in line/file
|
||||
|
||||
_carp sprintf $msg, scalar @_;
|
||||
|
||||
return $tb->ok(0);
|
||||
}
|
||||
|
||||
my( $got, $expected, $name ) = @_;
|
||||
|
||||
$tb->_unoverload_str( \$expected, \$got );
|
||||
|
||||
my $ok;
|
||||
if( !ref $got and !ref $expected ) { # neither is a reference
|
||||
$ok = $tb->is_eq( $got, $expected, $name );
|
||||
}
|
||||
elsif( !ref $got xor !ref $expected ) { # one's a reference, one isn't
|
||||
$ok = $tb->ok( 0, $name );
|
||||
$tb->diag( _format_stack({ vals => [ $got, $expected ] }) );
|
||||
}
|
||||
else { # both references
|
||||
local @Data_Stack = ();
|
||||
if( _deep_check( $got, $expected ) ) {
|
||||
$ok = $tb->ok( 1, $name );
|
||||
}
|
||||
else {
|
||||
$ok = $tb->ok( 0, $name );
|
||||
$tb->diag( _format_stack(@Data_Stack) );
|
||||
}
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
sub _format_stack {
|
||||
my(@Stack) = @_;
|
||||
|
||||
my $var = '$FOO';
|
||||
my $did_arrow = 0;
|
||||
foreach my $entry (@Stack) {
|
||||
my $type = $entry->{type} || '';
|
||||
my $idx = $entry->{'idx'};
|
||||
if( $type eq 'HASH' ) {
|
||||
$var .= "->" unless $did_arrow++;
|
||||
$var .= "{$idx}";
|
||||
}
|
||||
elsif( $type eq 'ARRAY' ) {
|
||||
$var .= "->" unless $did_arrow++;
|
||||
$var .= "[$idx]";
|
||||
}
|
||||
elsif( $type eq 'REF' ) {
|
||||
$var = "\${$var}";
|
||||
}
|
||||
}
|
||||
|
||||
my @vals = @{ $Stack[-1]{vals} }[ 0, 1 ];
|
||||
my @vars = ();
|
||||
( $vars[0] = $var ) =~ s/\$FOO/ \$got/;
|
||||
( $vars[1] = $var ) =~ s/\$FOO/\$expected/;
|
||||
|
||||
my $out = "Structures begin differing at:\n";
|
||||
foreach my $idx ( 0 .. $#vals ) {
|
||||
my $val = $vals[$idx];
|
||||
$vals[$idx]
|
||||
= !defined $val ? 'undef'
|
||||
: _dne($val) ? "Does not exist"
|
||||
: ref $val ? "$val"
|
||||
: "'$val'";
|
||||
}
|
||||
|
||||
$out .= "$vars[0] = $vals[0]\n";
|
||||
$out .= "$vars[1] = $vals[1]\n";
|
||||
|
||||
$out =~ s/^/ /msg;
|
||||
return $out;
|
||||
}
|
||||
|
||||
sub _type {
|
||||
my $thing = shift;
|
||||
|
||||
return '' if !ref $thing;
|
||||
|
||||
for my $type (qw(ARRAY HASH REF SCALAR GLOB CODE Regexp)) {
|
||||
return $type if UNIVERSAL::isa( $thing, $type );
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
#line 1059
|
||||
|
||||
sub diag {
|
||||
return Test::More->builder->diag(@_);
|
||||
}
|
||||
|
||||
sub note {
|
||||
return Test::More->builder->note(@_);
|
||||
}
|
||||
|
||||
#line 1085
|
||||
|
||||
sub explain {
|
||||
return Test::More->builder->explain(@_);
|
||||
}
|
||||
|
||||
#line 1151
|
||||
|
||||
## no critic (Subroutines::RequireFinalReturn)
|
||||
sub skip {
|
||||
my( $why, $how_many ) = @_;
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
unless( defined $how_many ) {
|
||||
# $how_many can only be avoided when no_plan is in use.
|
||||
_carp "skip() needs to know \$how_many tests are in the block"
|
||||
unless $tb->has_plan eq 'no_plan';
|
||||
$how_many = 1;
|
||||
}
|
||||
|
||||
if( defined $how_many and $how_many =~ /\D/ ) {
|
||||
_carp
|
||||
"skip() was passed a non-numeric number of tests. Did you get the arguments backwards?";
|
||||
$how_many = 1;
|
||||
}
|
||||
|
||||
for( 1 .. $how_many ) {
|
||||
$tb->skip($why);
|
||||
}
|
||||
|
||||
no warnings 'exiting';
|
||||
last SKIP;
|
||||
}
|
||||
|
||||
#line 1238
|
||||
|
||||
sub todo_skip {
|
||||
my( $why, $how_many ) = @_;
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
unless( defined $how_many ) {
|
||||
# $how_many can only be avoided when no_plan is in use.
|
||||
_carp "todo_skip() needs to know \$how_many tests are in the block"
|
||||
unless $tb->has_plan eq 'no_plan';
|
||||
$how_many = 1;
|
||||
}
|
||||
|
||||
for( 1 .. $how_many ) {
|
||||
$tb->todo_skip($why);
|
||||
}
|
||||
|
||||
no warnings 'exiting';
|
||||
last TODO;
|
||||
}
|
||||
|
||||
#line 1293
|
||||
|
||||
sub BAIL_OUT {
|
||||
my $reason = shift;
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
$tb->BAIL_OUT($reason);
|
||||
}
|
||||
|
||||
#line 1332
|
||||
|
||||
#'#
|
||||
sub eq_array {
|
||||
local @Data_Stack = ();
|
||||
_deep_check(@_);
|
||||
}
|
||||
|
||||
sub _eq_array {
|
||||
my( $a1, $a2 ) = @_;
|
||||
|
||||
if( grep _type($_) ne 'ARRAY', $a1, $a2 ) {
|
||||
warn "eq_array passed a non-array ref";
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1 if $a1 eq $a2;
|
||||
|
||||
my $ok = 1;
|
||||
my $max = $#$a1 > $#$a2 ? $#$a1 : $#$a2;
|
||||
for( 0 .. $max ) {
|
||||
my $e1 = $_ > $#$a1 ? $DNE : $a1->[$_];
|
||||
my $e2 = $_ > $#$a2 ? $DNE : $a2->[$_];
|
||||
|
||||
push @Data_Stack, { type => 'ARRAY', idx => $_, vals => [ $e1, $e2 ] };
|
||||
$ok = _deep_check( $e1, $e2 );
|
||||
pop @Data_Stack if $ok;
|
||||
|
||||
last unless $ok;
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
sub _deep_check {
|
||||
my( $e1, $e2 ) = @_;
|
||||
my $tb = Test::More->builder;
|
||||
|
||||
my $ok = 0;
|
||||
|
||||
# Effectively turn %Refs_Seen into a stack. This avoids picking up
|
||||
# the same referenced used twice (such as [\$a, \$a]) to be considered
|
||||
# circular.
|
||||
local %Refs_Seen = %Refs_Seen;
|
||||
|
||||
{
|
||||
# Quiet uninitialized value warnings when comparing undefs.
|
||||
no warnings 'uninitialized';
|
||||
|
||||
$tb->_unoverload_str( \$e1, \$e2 );
|
||||
|
||||
# Either they're both references or both not.
|
||||
my $same_ref = !( !ref $e1 xor !ref $e2 );
|
||||
my $not_ref = ( !ref $e1 and !ref $e2 );
|
||||
|
||||
if( defined $e1 xor defined $e2 ) {
|
||||
$ok = 0;
|
||||
}
|
||||
elsif( !defined $e1 and !defined $e2 ) {
|
||||
# Shortcut if they're both defined.
|
||||
$ok = 1;
|
||||
}
|
||||
elsif( _dne($e1) xor _dne($e2) ) {
|
||||
$ok = 0;
|
||||
}
|
||||
elsif( $same_ref and( $e1 eq $e2 ) ) {
|
||||
$ok = 1;
|
||||
}
|
||||
elsif($not_ref) {
|
||||
push @Data_Stack, { type => '', vals => [ $e1, $e2 ] };
|
||||
$ok = 0;
|
||||
}
|
||||
else {
|
||||
if( $Refs_Seen{$e1} ) {
|
||||
return $Refs_Seen{$e1} eq $e2;
|
||||
}
|
||||
else {
|
||||
$Refs_Seen{$e1} = "$e2";
|
||||
}
|
||||
|
||||
my $type = _type($e1);
|
||||
$type = 'DIFFERENT' unless _type($e2) eq $type;
|
||||
|
||||
if( $type eq 'DIFFERENT' ) {
|
||||
push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] };
|
||||
$ok = 0;
|
||||
}
|
||||
elsif( $type eq 'ARRAY' ) {
|
||||
$ok = _eq_array( $e1, $e2 );
|
||||
}
|
||||
elsif( $type eq 'HASH' ) {
|
||||
$ok = _eq_hash( $e1, $e2 );
|
||||
}
|
||||
elsif( $type eq 'REF' ) {
|
||||
push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] };
|
||||
$ok = _deep_check( $$e1, $$e2 );
|
||||
pop @Data_Stack if $ok;
|
||||
}
|
||||
elsif( $type eq 'SCALAR' ) {
|
||||
push @Data_Stack, { type => 'REF', vals => [ $e1, $e2 ] };
|
||||
$ok = _deep_check( $$e1, $$e2 );
|
||||
pop @Data_Stack if $ok;
|
||||
}
|
||||
elsif($type) {
|
||||
push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] };
|
||||
$ok = 0;
|
||||
}
|
||||
else {
|
||||
_whoa( 1, "No type in _deep_check" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
sub _whoa {
|
||||
my( $check, $desc ) = @_;
|
||||
if($check) {
|
||||
die <<"WHOA";
|
||||
WHOA! $desc
|
||||
This should never happen! Please contact the author immediately!
|
||||
WHOA
|
||||
}
|
||||
}
|
||||
|
||||
#line 1465
|
||||
|
||||
sub eq_hash {
|
||||
local @Data_Stack = ();
|
||||
return _deep_check(@_);
|
||||
}
|
||||
|
||||
sub _eq_hash {
|
||||
my( $a1, $a2 ) = @_;
|
||||
|
||||
if( grep _type($_) ne 'HASH', $a1, $a2 ) {
|
||||
warn "eq_hash passed a non-hash ref";
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1 if $a1 eq $a2;
|
||||
|
||||
my $ok = 1;
|
||||
my $bigger = keys %$a1 > keys %$a2 ? $a1 : $a2;
|
||||
foreach my $k ( keys %$bigger ) {
|
||||
my $e1 = exists $a1->{$k} ? $a1->{$k} : $DNE;
|
||||
my $e2 = exists $a2->{$k} ? $a2->{$k} : $DNE;
|
||||
|
||||
push @Data_Stack, { type => 'HASH', idx => $k, vals => [ $e1, $e2 ] };
|
||||
$ok = _deep_check( $e1, $e2 );
|
||||
pop @Data_Stack if $ok;
|
||||
|
||||
last unless $ok;
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
#line 1522
|
||||
|
||||
sub eq_set {
|
||||
my( $a1, $a2 ) = @_;
|
||||
return 0 unless @$a1 == @$a2;
|
||||
|
||||
no warnings 'uninitialized';
|
||||
|
||||
# It really doesn't matter how we sort them, as long as both arrays are
|
||||
# sorted with the same algorithm.
|
||||
#
|
||||
# Ensure that references are not accidentally treated the same as a
|
||||
# string containing the reference.
|
||||
#
|
||||
# Have to inline the sort routine due to a threading/sort bug.
|
||||
# See [rt.cpan.org 6782]
|
||||
#
|
||||
# I don't know how references would be sorted so we just don't sort
|
||||
# them. This means eq_set doesn't really work with refs.
|
||||
return eq_array(
|
||||
[ grep( ref, @$a1 ), sort( grep( !ref, @$a1 ) ) ],
|
||||
[ grep( ref, @$a2 ), sort( grep( !ref, @$a2 ) ) ],
|
||||
);
|
||||
}
|
||||
|
||||
#line 1735
|
||||
|
||||
1;
|
@ -0,0 +1,321 @@
|
||||
package Test::Nginx;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
our $VERSION = '0.18';
|
||||
|
||||
__END__
|
||||
|
||||
=encoding utf-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Test::Nginx - Testing modules for Nginx C module development
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This distribution provides two testing modules for Nginx C module development:
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
|
||||
L<Test::Nginx::LWP>
|
||||
|
||||
=item *
|
||||
|
||||
L<Test::Nginx::Socket>
|
||||
|
||||
=back
|
||||
|
||||
All of them are based on L<Test::Base>.
|
||||
|
||||
Usually, L<Test::Nginx::Socket> is preferred because it works on a much lower
|
||||
level and not that fault tolerant like L<Test::Nginx::LWP>.
|
||||
|
||||
Also, a lot of connection hang issues (like wrong C<< r->main->count >> value in nginx
|
||||
0.8.x) can only be captured by L<Test::Nginx::Socket> because Perl's L<LWP::UserAgent> client
|
||||
will close the connection itself which will conceal such issues from
|
||||
the testers.
|
||||
|
||||
Test::Nginx automatically starts an nginx instance (from the C<PATH> env)
|
||||
rooted at t/servroot/ and the default config template makes this nginx
|
||||
instance listen on the port C<1984> by default. One can specify a different
|
||||
port number by setting his port number to the C<TEST_NGINX_PORT> environment,
|
||||
as in
|
||||
|
||||
export TEST_NGINX_PORT=1989
|
||||
|
||||
=head2 etcproxy integration
|
||||
|
||||
The default settings in etcproxy (https://github.com/chaoslawful/etcproxy)
|
||||
makes this small TCP proxy split the TCP packets into bytes and introduce 1 ms latency among them.
|
||||
|
||||
There's usually various TCP chains that we can put etcproxy into, for example
|
||||
|
||||
=head3 Test::Nginx <=> nginx
|
||||
|
||||
$ ./etcproxy 1234 1984
|
||||
|
||||
Here we tell etcproxy to listen on port 1234 and to delegate all the
|
||||
TCP traffic to the port 1984, the default port that Test::Nginx makes
|
||||
nginx listen to.
|
||||
|
||||
And then we tell Test::Nginx to test against the port 1234, where
|
||||
etcproxy listens on, rather than the port 1984 that nginx directly
|
||||
listens on:
|
||||
|
||||
$ TEST_NGINX_CLIENT_PORT=1234 prove -r t/
|
||||
|
||||
Then the TCP chain now looks like this:
|
||||
|
||||
Test::Nginx <=> etcproxy (1234) <=> nginx (1984)
|
||||
|
||||
So etcproxy can effectively emulate extreme network conditions and
|
||||
exercise "unusual" code paths in your nginx server by your tests.
|
||||
|
||||
In practice, *tons* of weird bugs can be captured by this setting.
|
||||
Even ourselves didn't expect that this simple approach is so
|
||||
effective.
|
||||
|
||||
=head3 nginx <=> memcached
|
||||
|
||||
We first start the memcached server daemon on port 11211:
|
||||
|
||||
memcached -p 11211 -vv
|
||||
|
||||
and then we another etcproxy instance to listen on port 11984 like this
|
||||
|
||||
$ ./etcproxy 11984 11211
|
||||
|
||||
Then we tell our t/foo.t test script to connect to 11984 rather than 11211:
|
||||
|
||||
# foo.t
|
||||
use Test::Nginx::Socket;
|
||||
repeat_each(1);
|
||||
plan tests => 2 * repeat_each() * blocks();
|
||||
$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; # make this env take a default value
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: sanity
|
||||
--- config
|
||||
location /foo {
|
||||
set $memc_cmd set;
|
||||
set $memc_key foo;
|
||||
set $memc_value bar;
|
||||
memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;
|
||||
}
|
||||
--- request
|
||||
GET /foo
|
||||
--- response_body_like: STORED
|
||||
|
||||
The Test::Nginx library will automatically expand the special macro
|
||||
C<$TEST_NGINX_MEMCACHED_PORT> to the environment with the same name.
|
||||
You can define your own C<$TEST_NGINX_BLAH_BLAH_PORT> macros as long as
|
||||
its prefix is C<TEST_NGINX_> and all in upper case letters.
|
||||
|
||||
And now we can run your test script against the etcproxy port 11984:
|
||||
|
||||
TEST_NGINX_MEMCACHED_PORT=11984 prove t/foo.t
|
||||
|
||||
Then the TCP chains look like this:
|
||||
|
||||
Test::Nginx <=> nginx (1984) <=> etcproxy (11984) <=> memcached (11211)
|
||||
|
||||
If C<TEST_NGINX_MEMCACHED_PORT> is not set, then it will take the default
|
||||
value 11211, which is what we want when there's no etcproxy
|
||||
configured:
|
||||
|
||||
Test::Nginx <=> nginx (1984) <=> memcached (11211)
|
||||
|
||||
This approach also works for proxied mysql and postgres traffic.
|
||||
Please see the live test suite of ngx_drizzle and ngx_postgres for
|
||||
more details.
|
||||
|
||||
Usually we set both C<TEST_NGINX_CLIENT_PORT> and
|
||||
C<TEST_NGINX_MEMCACHED_PORT> (and etc) at the same time, effectively
|
||||
yielding the following chain:
|
||||
|
||||
Test::Nginx <=> etcproxy (1234) <=> nginx (1984) <=> etcproxy (11984) <=> memcached (11211)
|
||||
|
||||
as long as you run two separate etcproxy instances in two separate terminals.
|
||||
|
||||
It's easy to verify if the traffic actually goes through your etcproxy
|
||||
server. Just check if the terminal running etcproxy emits outputs. By
|
||||
default, etcproxy always dump out the incoming and outgoing data to
|
||||
stdout/stderr.
|
||||
|
||||
=head2 valgrind integration
|
||||
|
||||
Test::Nginx has integrated support for valgrind (L<http://valgrind.org>) even though by
|
||||
default it does not bother running it with the tests because valgrind
|
||||
will significantly slow down the test sutie.
|
||||
|
||||
First ensure that your valgrind executable visible in your PATH env.
|
||||
And then run your test suite with the C<TEST_NGINX_USE_VALGRIND> env set
|
||||
to true:
|
||||
|
||||
TEST_NGINX_USE_VALGRIND=1 prove -r t
|
||||
|
||||
If you see false alarms, you do have a chance to skip them by defining
|
||||
a ./valgrind.suppress file at the root of your module source tree, as
|
||||
in
|
||||
|
||||
L<https://github.com/chaoslawful/drizzle-nginx-module/blob/master/valgrind.suppress>
|
||||
|
||||
This is the suppression file for ngx_drizzle. Test::Nginx will
|
||||
automatically use it to start nginx with valgrind memcheck if this
|
||||
file does exist at the expected location.
|
||||
|
||||
If you do see a lot of "Connection refused" errors while running the
|
||||
tests this way, then you probably have a slow machine (or a very busy
|
||||
one) that the default waiting time is not sufficient for valgrind to
|
||||
start. You can define the sleep time to a larger value by setting the
|
||||
C<TEST_NGINX_SLEEP> env:
|
||||
|
||||
TEST_NGINX_SLEEP=1 prove -r t
|
||||
|
||||
The time unit used here is "second". The default sleep setting just
|
||||
fits my ThinkPad (C<Core2Duo T9600>).
|
||||
|
||||
Applying the no-pool patch to your nginx core is recommended while
|
||||
running nginx with valgrind:
|
||||
|
||||
L<https://github.com/shrimp/no-pool-nginx>
|
||||
|
||||
The nginx memory pool can prevent valgrind from spotting lots of
|
||||
invalid memory reads/writes as well as certain double-free errors. We
|
||||
did find a lot more memory issues in many of our modules when we first
|
||||
introduced the no-pool patch in practice ;)
|
||||
|
||||
There's also more advanced features in Test::Nginx that have never
|
||||
documented. I'd like to write more about them in the near future ;)
|
||||
|
||||
|
||||
=head1 Nginx C modules that use Test::Nginx to drive their test suites
|
||||
|
||||
=over
|
||||
|
||||
=item ngx_echo
|
||||
|
||||
L<http://github.com/agentzh/echo-nginx-module>
|
||||
|
||||
=item ngx_headers_more
|
||||
|
||||
L<http://github.com/agentzh/headers-more-nginx-module>
|
||||
|
||||
=item ngx_chunkin
|
||||
|
||||
L<http://wiki.nginx.org/NginxHttpChunkinModule>
|
||||
|
||||
=item ngx_memc
|
||||
|
||||
L<http://wiki.nginx.org/NginxHttpMemcModule>
|
||||
|
||||
=item ngx_drizzle
|
||||
|
||||
L<http://github.com/chaoslawful/drizzle-nginx-module>
|
||||
|
||||
=item ngx_rds_json
|
||||
|
||||
L<http://github.com/agentzh/rds-json-nginx-module>
|
||||
|
||||
=item ngx_rds_csv
|
||||
|
||||
L<http://github.com/agentzh/rds-csv-nginx-module>
|
||||
|
||||
=item ngx_xss
|
||||
|
||||
L<http://github.com/agentzh/xss-nginx-module>
|
||||
|
||||
=item ngx_srcache
|
||||
|
||||
L<http://github.com/agentzh/srcache-nginx-module>
|
||||
|
||||
=item ngx_lua
|
||||
|
||||
L<http://github.com/chaoslawful/lua-nginx-module>
|
||||
|
||||
=item ngx_set_misc
|
||||
|
||||
L<http://github.com/agentzh/set-misc-nginx-module>
|
||||
|
||||
=item ngx_array_var
|
||||
|
||||
L<http://github.com/agentzh/array-var-nginx-module>
|
||||
|
||||
=item ngx_form_input
|
||||
|
||||
L<http://github.com/calio/form-input-nginx-module>
|
||||
|
||||
=item ngx_iconv
|
||||
|
||||
L<http://github.com/calio/iconv-nginx-module>
|
||||
|
||||
=item ngx_set_cconv
|
||||
|
||||
L<http://github.com/liseen/set-cconv-nginx-module>
|
||||
|
||||
=item ngx_postgres
|
||||
|
||||
L<http://github.com/FRiCKLE/ngx_postgres>
|
||||
|
||||
=item ngx_coolkit
|
||||
|
||||
L<http://github.com/FRiCKLE/ngx_coolkit>
|
||||
|
||||
=item Naxsi
|
||||
|
||||
L<http://code.google.com/p/naxsi/>
|
||||
|
||||
=back
|
||||
|
||||
=head1 SOURCE REPOSITORY
|
||||
|
||||
This module has a Git repository on Github, which has access for all.
|
||||
|
||||
http://github.com/agentzh/test-nginx
|
||||
|
||||
If you want a commit bit, feel free to drop me a line.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
agentzh (章亦春) C<< <agentzh@gmail.com> >>
|
||||
|
||||
Antoine BONAVITA C<< <antoine.bonavita@gmail.com> >>
|
||||
|
||||
=head1 COPYRIGHT & LICENSE
|
||||
|
||||
Copyright (c) 2009-2012, agentzh C<< <agentzh@gmail.com> >>.
|
||||
|
||||
Copyright (c) 2011-2012, Antoine Bonavita C<< <antoine.bonavita@gmail.com> >>.
|
||||
|
||||
This module is licensed under the terms of the BSD license.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
=item *
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
=item *
|
||||
|
||||
Neither the name of the authors nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
=back
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Test::Nginx::LWP>, L<Test::Nginx::Socket>, L<Test::Base>.
|
||||
|
@ -0,0 +1,506 @@
|
||||
package Test::Nginx::LWP;
|
||||
|
||||
use lib 'lib';
|
||||
use lib 'inc';
|
||||
use Test::Base -Base;
|
||||
|
||||
our $VERSION = '0.18';
|
||||
|
||||
our $NoLongString;
|
||||
|
||||
use LWP::UserAgent;
|
||||
use Time::HiRes qw(sleep);
|
||||
use Test::LongString;
|
||||
use Test::Nginx::Util qw(
|
||||
setup_server_root
|
||||
write_config_file
|
||||
get_canon_version
|
||||
get_nginx_version
|
||||
trim
|
||||
show_all_chars
|
||||
parse_headers
|
||||
run_tests
|
||||
$ServerPortForClient
|
||||
$PidFile
|
||||
$ServRoot
|
||||
$ConfFile
|
||||
$ServerPort
|
||||
$RunTestHelper
|
||||
$NoNginxManager
|
||||
$RepeatEach
|
||||
worker_connections
|
||||
master_process_enabled
|
||||
master_on
|
||||
master_off
|
||||
config_preamble
|
||||
repeat_each
|
||||
no_shuffle
|
||||
no_root_location
|
||||
);
|
||||
|
||||
our $UserAgent = LWP::UserAgent->new;
|
||||
$UserAgent->agent(__PACKAGE__);
|
||||
#$UserAgent->default_headers(HTTP::Headers->new);
|
||||
|
||||
#use Smart::Comments::JSON '##';
|
||||
|
||||
our @EXPORT = qw( plan run_tests run_test
|
||||
repeat_each config_preamble worker_connections
|
||||
master_process_enabled master_on master_off
|
||||
no_long_string no_shuffle no_root_location);
|
||||
|
||||
sub no_long_string () {
|
||||
$NoLongString = 1;
|
||||
}
|
||||
|
||||
sub run_test_helper ($$);
|
||||
|
||||
$RunTestHelper = \&run_test_helper;
|
||||
|
||||
sub parse_request ($$) {
|
||||
my ($name, $rrequest) = @_;
|
||||
open my $in, '<', $rrequest;
|
||||
my $first = <$in>;
|
||||
if (!$first) {
|
||||
Test::More::BAIL_OUT("$name - Request line should be non-empty");
|
||||
die;
|
||||
}
|
||||
$first =~ s/^\s+|\s+$//g;
|
||||
my ($meth, $rel_url) = split /\s+/, $first, 2;
|
||||
my $url = "http://localhost:$ServerPortForClient" . $rel_url;
|
||||
|
||||
my $content = do { local $/; <$in> };
|
||||
if ($content) {
|
||||
$content =~ s/^\s+|\s+$//s;
|
||||
}
|
||||
|
||||
close $in;
|
||||
|
||||
return {
|
||||
method => $meth,
|
||||
url => $url,
|
||||
content => $content,
|
||||
};
|
||||
}
|
||||
|
||||
sub chunk_it ($$$) {
|
||||
my ($chunks, $start_delay, $middle_delay) = @_;
|
||||
my $i = 0;
|
||||
return sub {
|
||||
if ($i == 0) {
|
||||
if ($start_delay) {
|
||||
sleep($start_delay);
|
||||
}
|
||||
} elsif ($middle_delay) {
|
||||
sleep($middle_delay);
|
||||
}
|
||||
return $chunks->[$i++];
|
||||
}
|
||||
}
|
||||
|
||||
sub run_test_helper ($$) {
|
||||
my ($block, $dry_run) = @_;
|
||||
|
||||
my $request = $block->request;
|
||||
|
||||
my $name = $block->name;
|
||||
#if (defined $TODO) {
|
||||
#$name .= "# $TODO";
|
||||
#}
|
||||
|
||||
my $req_spec = parse_request($name, \$request);
|
||||
## $req_spec
|
||||
my $method = $req_spec->{method};
|
||||
my $req = HTTP::Request->new($method);
|
||||
my $content = $req_spec->{content};
|
||||
|
||||
if (defined ($block->request_headers)) {
|
||||
my $headers = parse_headers($block->request_headers);
|
||||
while (my ($key, $val) = each %$headers) {
|
||||
$req->header($key => $val);
|
||||
}
|
||||
}
|
||||
|
||||
#$req->header('Accept', '*/*');
|
||||
$req->url($req_spec->{url});
|
||||
if ($content) {
|
||||
if ($method eq 'GET' or $method eq 'HEAD') {
|
||||
croak "HTTP 1.0/1.1 $method request should not have content: $content";
|
||||
}
|
||||
$req->content($content);
|
||||
} elsif ($method eq 'POST' or $method eq 'PUT') {
|
||||
my $chunks = $block->chunked_body;
|
||||
if (defined $chunks) {
|
||||
if (!ref $chunks or ref $chunks ne 'ARRAY') {
|
||||
|
||||
Test::More::BAIL_OUT("$name - --- chunked_body should takes a Perl array ref as its value");
|
||||
}
|
||||
|
||||
my $start_delay = $block->start_chunk_delay || 0;
|
||||
my $middle_delay = $block->middle_chunk_delay || 0;
|
||||
$req->content(chunk_it($chunks, $start_delay, $middle_delay));
|
||||
if (!defined $req->header('Content-Type')) {
|
||||
$req->header('Content-Type' => 'text/plain');
|
||||
}
|
||||
} else {
|
||||
if (!defined $req->header('Content-Type')) {
|
||||
$req->header('Content-Type' => 'text/plain');
|
||||
}
|
||||
|
||||
$req->header('Content-Length' => 0);
|
||||
}
|
||||
}
|
||||
|
||||
if ($block->more_headers) {
|
||||
my @headers = split /\n+/, $block->more_headers;
|
||||
for my $header (@headers) {
|
||||
next if $header =~ /^\s*\#/;
|
||||
my ($key, $val) = split /:\s*/, $header, 2;
|
||||
#warn "[$key, $val]\n";
|
||||
$req->header($key => $val);
|
||||
}
|
||||
}
|
||||
|
||||
#warn "req: ", $req->as_string, "\n";
|
||||
#warn "DONE!!!!!!!!!!!!!!!!!!!!";
|
||||
|
||||
my $res = HTTP::Response->new;
|
||||
unless ($dry_run) {
|
||||
$res = $UserAgent->request($req);
|
||||
}
|
||||
|
||||
#warn "res returned!!!";
|
||||
|
||||
if ($dry_run) {
|
||||
SKIP: {
|
||||
Test::More::skip("$name - tests skipped due to the lack of directive $dry_run", 1);
|
||||
}
|
||||
} else {
|
||||
if (defined $block->error_code) {
|
||||
is($res->code, $block->error_code, "$name - status code ok");
|
||||
} else {
|
||||
is($res->code, 200, "$name - status code ok");
|
||||
}
|
||||
}
|
||||
|
||||
if (defined $block->response_headers) {
|
||||
my $headers = parse_headers($block->response_headers);
|
||||
while (my ($key, $val) = each %$headers) {
|
||||
my $expected_val = $res->header($key);
|
||||
if (!defined $expected_val) {
|
||||
$expected_val = '';
|
||||
}
|
||||
if ($dry_run) {
|
||||
SKIP: {
|
||||
Test::More::skip("$name - tests skipped due to the lack of directive $dry_run", 1);
|
||||
}
|
||||
} else {
|
||||
is $expected_val, $val,
|
||||
"$name - header $key ok";
|
||||
}
|
||||
}
|
||||
} elsif (defined $block->response_headers_like) {
|
||||
my $headers = parse_headers($block->response_headers_like);
|
||||
while (my ($key, $val) = each %$headers) {
|
||||
my $expected_val = $res->header($key);
|
||||
if (!defined $expected_val) {
|
||||
$expected_val = '';
|
||||
}
|
||||
if ($dry_run) {
|
||||
SKIP: {
|
||||
Test::More::skip("$name - tests skipped due to the lack of directive $dry_run", 1);
|
||||
}
|
||||
} else {
|
||||
like $expected_val, qr/^$val$/,
|
||||
"$name - header $key like ok";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (defined $block->response_body) {
|
||||
my $content = $res->content;
|
||||
if (defined $content) {
|
||||
$content =~ s/^TE: deflate,gzip;q=0\.3\r\n//gms;
|
||||
}
|
||||
|
||||
$content =~ s/^Connection: TE, close\r\n//gms;
|
||||
my $expected = $block->response_body;
|
||||
$expected =~ s/\$ServerPort\b/$ServerPort/g;
|
||||
$expected =~ s/\$ServerPortForClient\b/$ServerPortForClient/g;
|
||||
#warn show_all_chars($content);
|
||||
|
||||
if ($dry_run) {
|
||||
SKIP: {
|
||||
Test::More::skip("$name - tests skipped due to the lack of directive $dry_run", 1);
|
||||
}
|
||||
} else {
|
||||
if ($NoLongString) {
|
||||
is($content, $expected, "$name - response_body - response is expected");
|
||||
} else {
|
||||
is_string($content, $expected, "$name - response_body - response is expected");
|
||||
}
|
||||
#is($content, $expected, "$name - response_body - response is expected");
|
||||
}
|
||||
|
||||
} elsif (defined $block->response_body_like) {
|
||||
my $content = $res->content;
|
||||
if (defined $content) {
|
||||
$content =~ s/^TE: deflate,gzip;q=0\.3\r\n//gms;
|
||||
}
|
||||
$content =~ s/^Connection: TE, close\r\n//gms;
|
||||
my $expected_pat = $block->response_body_like;
|
||||
$expected_pat =~ s/\$ServerPort\b/$ServerPort/g;
|
||||
$expected_pat =~ s/\$ServerPortForClient\b/$ServerPortForClient/g;
|
||||
my $summary = trim($content);
|
||||
|
||||
if ($dry_run) {
|
||||
SKIP: {
|
||||
Test::More::skip("$name - tests skipped due to the lack of directive $dry_run", 1);
|
||||
}
|
||||
} else {
|
||||
like($content, qr/$expected_pat/s, "$name - response_body_like - response is expected ($summary)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=encoding utf-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Test::Nginx::LWP - LWP-backed test scaffold for the Nginx C modules
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Test::Nginx::LWP;
|
||||
|
||||
plan tests => $Test::Nginx::LWP::RepeatEach * 2 * blocks();
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: sanity
|
||||
--- config
|
||||
location /echo {
|
||||
echo_before_body hello;
|
||||
echo world;
|
||||
}
|
||||
--- request
|
||||
GET /echo
|
||||
--- response_body
|
||||
hello
|
||||
world
|
||||
--- error_code: 200
|
||||
|
||||
|
||||
=== TEST 2: set Server
|
||||
--- config
|
||||
location /foo {
|
||||
echo hi;
|
||||
more_set_headers 'Server: Foo';
|
||||
}
|
||||
--- request
|
||||
GET /foo
|
||||
--- response_headers
|
||||
Server: Foo
|
||||
--- response_body
|
||||
hi
|
||||
|
||||
|
||||
=== TEST 3: clear Server
|
||||
--- config
|
||||
location /foo {
|
||||
echo hi;
|
||||
more_clear_headers 'Server: ';
|
||||
}
|
||||
--- request
|
||||
GET /foo
|
||||
--- response_headers_like
|
||||
Server: nginx.*
|
||||
--- response_body
|
||||
hi
|
||||
|
||||
|
||||
=== TEST 4: set request header at client side and rewrite it
|
||||
--- config
|
||||
location /foo {
|
||||
more_set_input_headers 'X-Foo: howdy';
|
||||
echo $http_x_foo;
|
||||
}
|
||||
--- request
|
||||
GET /foo
|
||||
--- request_headers
|
||||
X-Foo: blah
|
||||
--- response_headers
|
||||
X-Foo:
|
||||
--- response_body
|
||||
howdy
|
||||
|
||||
|
||||
=== TEST 3: rewrite content length
|
||||
--- config
|
||||
location /bar {
|
||||
more_set_input_headers 'Content-Length: 2048';
|
||||
echo_read_request_body;
|
||||
echo_request_body;
|
||||
}
|
||||
--- request eval
|
||||
"POST /bar\n" .
|
||||
"a" x 4096
|
||||
--- response_body eval
|
||||
"a" x 2048
|
||||
|
||||
|
||||
=== TEST 4: timer without explicit reset
|
||||
--- config
|
||||
location /timer {
|
||||
echo_sleep 0.03;
|
||||
echo "elapsed $echo_timer_elapsed sec.";
|
||||
}
|
||||
--- request
|
||||
GET /timer
|
||||
--- response_body_like
|
||||
^elapsed 0\.0(2[6-9]|3[0-6]) sec\.$
|
||||
|
||||
|
||||
=== TEST 5: small buf (using 2-byte buf)
|
||||
--- config
|
||||
chunkin on;
|
||||
location /main {
|
||||
client_body_buffer_size 2;
|
||||
echo "body:";
|
||||
echo $echo_request_body;
|
||||
echo_request_body;
|
||||
}
|
||||
--- request
|
||||
POST /main
|
||||
--- start_chunk_delay: 0.01
|
||||
--- middle_chunk_delay: 0.01
|
||||
--- chunked_body eval
|
||||
["hello", "world"]
|
||||
--- error_code: 200
|
||||
--- response_body eval
|
||||
"body:
|
||||
|
||||
helloworld"
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module provides a test scaffold based on L<LWP::UserAgent> for automated testing in Nginx C module development.
|
||||
|
||||
This class inherits from L<Test::Base>, thus bringing all its
|
||||
declarative power to the Nginx C module testing practices.
|
||||
|
||||
You need to terminate or kill any Nginx processes before running the test suite if you have changed the Nginx server binary. Normally it's as simple as
|
||||
|
||||
killall nginx
|
||||
PATH=/path/to/your/nginx-with-memc-module:$PATH prove -r t
|
||||
|
||||
This module will create a temporary server root under t/servroot/ of the current working directory and starts and uses the nginx executable in the PATH environment.
|
||||
|
||||
You will often want to look into F<t/servroot/logs/error.log>
|
||||
when things go wrong ;)
|
||||
|
||||
=head1 Sections supported
|
||||
|
||||
The following sections are supported:
|
||||
|
||||
=over
|
||||
|
||||
=item config
|
||||
|
||||
=item http_config
|
||||
|
||||
=item request
|
||||
|
||||
=item request_headers
|
||||
|
||||
=item more_headers
|
||||
|
||||
=item response_body
|
||||
|
||||
=item response_body_like
|
||||
|
||||
=item response_headers
|
||||
|
||||
=item response_headers_like
|
||||
|
||||
=item error_code
|
||||
|
||||
=item chunked_body
|
||||
|
||||
=item middle_chunk_delay
|
||||
|
||||
=item start_chunk_delay
|
||||
|
||||
=back
|
||||
|
||||
=head1 Samples
|
||||
|
||||
You'll find live samples in the following Nginx 3rd-party modules:
|
||||
|
||||
=over
|
||||
|
||||
=item ngx_echo
|
||||
|
||||
L<http://wiki.nginx.org/NginxHttpEchoModule>
|
||||
|
||||
=item ngx_headers_more
|
||||
|
||||
L<http://wiki.nginx.org/NginxHttpHeadersMoreModule>
|
||||
|
||||
=item ngx_chunkin
|
||||
|
||||
L<http://wiki.nginx.org/NginxHttpChunkinModule>
|
||||
|
||||
=item ngx_memc
|
||||
|
||||
L<http://wiki.nginx.org/NginxHttpMemcModule>
|
||||
|
||||
=back
|
||||
|
||||
=head1 SOURCE REPOSITORY
|
||||
|
||||
This module has a Git repository on Github, which has access for all.
|
||||
|
||||
http://github.com/agentzh/test-nginx
|
||||
|
||||
If you want a commit bit, feel free to drop me a line.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
agentzh (章亦春) C<< <agentzh@gmail.com> >>
|
||||
|
||||
=head1 COPYRIGHT & LICENSE
|
||||
|
||||
Copyright (c) 2009-2012, agentzh C<< <agentzh@gmail.com> >>.
|
||||
|
||||
This module is licensed under the terms of the BSD license.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
=item *
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
=item *
|
||||
|
||||
Neither the name of the authors nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
=back
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Test::Nginx::Socket>, L<Test::Base>.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,982 @@
|
||||
package Test::Nginx::Util;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
our $VERSION = '0.18';
|
||||
|
||||
use base 'Exporter';
|
||||
|
||||
use POSIX qw( SIGQUIT SIGKILL SIGTERM SIGHUP );
|
||||
use File::Spec ();
|
||||
use HTTP::Response;
|
||||
use Cwd qw( cwd );
|
||||
use List::Util qw( shuffle );
|
||||
use Time::HiRes qw( sleep );
|
||||
use ExtUtils::MakeMaker ();
|
||||
use File::Path qw(make_path);
|
||||
|
||||
our $UseHup = $ENV{TEST_NGINX_USE_HUP};
|
||||
|
||||
our $Verbose = $ENV{TEST_NGINX_VERBOSE};
|
||||
|
||||
our $LatestNginxVersion = 0.008039;
|
||||
|
||||
our $NoNginxManager = $ENV{TEST_NGINX_NO_NGINX_MANAGER} || 0;
|
||||
our $Profiling = 0;
|
||||
|
||||
our $RepeatEach = 1;
|
||||
our $MAX_PROCESSES = 10;
|
||||
|
||||
our $NoShuffle = $ENV{TEST_NGINX_NO_SHUFFLE} || 0;
|
||||
|
||||
our $UseValgrind = $ENV{TEST_NGINX_USE_VALGRIND};
|
||||
|
||||
our $EventType = $ENV{TEST_NGINX_EVENT_TYPE};
|
||||
|
||||
our $PostponeOutput = $ENV{TEST_NGINX_POSTPONE_OUTPUT};
|
||||
|
||||
sub no_shuffle () {
|
||||
$NoShuffle = 1;
|
||||
}
|
||||
|
||||
sub no_nginx_manager () {
|
||||
$NoNginxManager = 1;
|
||||
}
|
||||
|
||||
our $ForkManager;
|
||||
|
||||
if ($Profiling || $UseValgrind) {
|
||||
eval "use Parallel::ForkManager";
|
||||
if ($@) {
|
||||
die "Failed to load Parallel::ForkManager: $@\n";
|
||||
}
|
||||
$ForkManager = new Parallel::ForkManager($MAX_PROCESSES);
|
||||
}
|
||||
|
||||
our $NginxBinary = $ENV{TEST_NGINX_BINARY} || 'nginx';
|
||||
our $Workers = 1;
|
||||
our $WorkerConnections = 64;
|
||||
our $LogLevel = $ENV{TEST_NGINX_LOG_LEVEL} || 'debug';
|
||||
our $MasterProcessEnabled = $ENV{TEST_NGINX_MASTER_PROCESS} || 'off';
|
||||
our $DaemonEnabled = 'on';
|
||||
our $ServerPort = $ENV{TEST_NGINX_SERVER_PORT} || $ENV{TEST_NGINX_PORT} || 1984;
|
||||
our $ServerPortForClient = $ENV{TEST_NGINX_CLIENT_PORT} || $ENV{TEST_NGINX_PORT} || 1984;
|
||||
our $NoRootLocation = 0;
|
||||
our $TestNginxSleep = $ENV{TEST_NGINX_SLEEP} || 0;
|
||||
our $BuildSlaveName = $ENV{TEST_NGINX_BUILDSLAVE};
|
||||
our $ForceRestartOnTest = (defined $ENV{TEST_NGINX_FORCE_RESTART_ON_TEST})
|
||||
? $ENV{TEST_NGINX_FORCE_RESTART_ON_TEST} : 1;
|
||||
|
||||
sub server_port (@) {
|
||||
if (@_) {
|
||||
$ServerPort = shift;
|
||||
} else {
|
||||
$ServerPort;
|
||||
}
|
||||
}
|
||||
|
||||
sub repeat_each (@) {
|
||||
if (@_) {
|
||||
$RepeatEach = shift;
|
||||
} else {
|
||||
return $RepeatEach;
|
||||
}
|
||||
}
|
||||
|
||||
sub worker_connections (@) {
|
||||
if (@_) {
|
||||
$WorkerConnections = shift;
|
||||
} else {
|
||||
return $WorkerConnections;
|
||||
}
|
||||
}
|
||||
|
||||
sub no_root_location () {
|
||||
$NoRootLocation = 1;
|
||||
}
|
||||
|
||||
sub workers (@) {
|
||||
if (@_) {
|
||||
#warn "setting workers to $_[0]";
|
||||
$Workers = shift;
|
||||
} else {
|
||||
return $Workers;
|
||||
}
|
||||
}
|
||||
|
||||
sub log_level (@) {
|
||||
if (@_) {
|
||||
$LogLevel = shift;
|
||||
} else {
|
||||
return $LogLevel;
|
||||
}
|
||||
}
|
||||
|
||||
sub master_on () {
|
||||
$MasterProcessEnabled = 'on';
|
||||
}
|
||||
|
||||
sub master_off () {
|
||||
$MasterProcessEnabled = 'off';
|
||||
}
|
||||
|
||||
sub master_process_enabled (@) {
|
||||
if (@_) {
|
||||
$MasterProcessEnabled = shift() ? 'on' : 'off';
|
||||
} else {
|
||||
return $MasterProcessEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
our @EXPORT_OK = qw(
|
||||
error_log_data
|
||||
setup_server_root
|
||||
write_config_file
|
||||
get_canon_version
|
||||
get_nginx_version
|
||||
trim
|
||||
show_all_chars
|
||||
parse_headers
|
||||
run_tests
|
||||
$ServerPortForClient
|
||||
$ServerPort
|
||||
$NginxVersion
|
||||
$PidFile
|
||||
$ServRoot
|
||||
$ConfFile
|
||||
$RunTestHelper
|
||||
$NoNginxManager
|
||||
$RepeatEach
|
||||
worker_connections
|
||||
workers
|
||||
master_on
|
||||
master_off
|
||||
config_preamble
|
||||
repeat_each
|
||||
master_process_enabled
|
||||
log_level
|
||||
no_shuffle
|
||||
no_root_location
|
||||
html_dir
|
||||
server_root
|
||||
server_port
|
||||
no_nginx_manager
|
||||
);
|
||||
|
||||
|
||||
if ($Profiling || $UseValgrind) {
|
||||
$DaemonEnabled = 'off';
|
||||
$MasterProcessEnabled = 'off';
|
||||
}
|
||||
|
||||
our $ConfigPreamble = '';
|
||||
|
||||
sub config_preamble ($) {
|
||||
$ConfigPreamble = shift;
|
||||
}
|
||||
|
||||
our $RunTestHelper;
|
||||
|
||||
our $NginxVersion;
|
||||
our $NginxRawVersion;
|
||||
our $TODO;
|
||||
|
||||
#our ($PrevRequest)
|
||||
our $PrevConfig;
|
||||
|
||||
our $ServRoot = $ENV{TEST_NGINX_SERVROOT} || File::Spec->catfile(cwd() || '.', 't/servroot');
|
||||
our $LogDir = File::Spec->catfile($ServRoot, 'logs');
|
||||
our $ErrLogFile = File::Spec->catfile($LogDir, 'error.log');
|
||||
our $AccLogFile = File::Spec->catfile($LogDir, 'access.log');
|
||||
our $HtmlDir = File::Spec->catfile($ServRoot, 'html');
|
||||
our $ConfDir = File::Spec->catfile($ServRoot, 'conf');
|
||||
our $ConfFile = File::Spec->catfile($ConfDir, 'nginx.conf');
|
||||
our $PidFile = File::Spec->catfile($LogDir, 'nginx.pid');
|
||||
|
||||
sub html_dir () {
|
||||
return $HtmlDir;
|
||||
}
|
||||
|
||||
sub server_root () {
|
||||
return $ServRoot;
|
||||
}
|
||||
|
||||
sub bail_out ($) {
|
||||
Test::More::BAIL_OUT(@_);
|
||||
}
|
||||
|
||||
sub error_log_data () {
|
||||
open my $in, $ErrLogFile or
|
||||
return undef;
|
||||
my @lines = <$in>;
|
||||
close $in;
|
||||
return \@lines;
|
||||
}
|
||||
|
||||
sub run_tests () {
|
||||
$NginxVersion = get_nginx_version();
|
||||
|
||||
if (defined $NginxVersion) {
|
||||
#warn "[INFO] Using nginx version $NginxVersion ($NginxRawVersion)\n";
|
||||
}
|
||||
|
||||
for my $block ($NoShuffle ? Test::Base::blocks() : shuffle Test::Base::blocks()) {
|
||||
#for (1..3) {
|
||||
run_test($block);
|
||||
#}
|
||||
}
|
||||
|
||||
if ($Profiling || $UseValgrind) {
|
||||
$ForkManager->wait_all_children;
|
||||
}
|
||||
}
|
||||
|
||||
sub setup_server_root () {
|
||||
if (-d $ServRoot) {
|
||||
# Take special care, so we won't accidentally remove
|
||||
# real user data when TEST_NGINX_SERVROOT is mis-used.
|
||||
system("rm -rf $ConfDir > /dev/null") == 0 or
|
||||
die "Can't remove $ConfDir";
|
||||
system("rm -rf $HtmlDir > /dev/null") == 0 or
|
||||
die "Can't remove $HtmlDir";
|
||||
system("rm -rf $LogDir > /dev/null") == 0 or
|
||||
die "Can't remove $LogDir";
|
||||
system("rm -rf $ServRoot/*_temp > /dev/null") == 0 or
|
||||
die "Can't remove $ServRoot/*_temp";
|
||||
system("rmdir $ServRoot > /dev/null") == 0 or
|
||||
die "Can't remove $ServRoot (not empty?)";
|
||||
}
|
||||
mkdir $ServRoot or
|
||||
die "Failed to do mkdir $ServRoot\n";
|
||||
mkdir $LogDir or
|
||||
die "Failed to do mkdir $LogDir\n";
|
||||
mkdir $HtmlDir or
|
||||
die "Failed to do mkdir $HtmlDir\n";
|
||||
|
||||
my $index_file = "$HtmlDir/index.html";
|
||||
|
||||
open my $out, ">$index_file" or
|
||||
die "Can't open $index_file for writing: $!\n";
|
||||
|
||||
print $out '<html><head><title>It works!</title></head><body>It works!</body></html>';
|
||||
|
||||
close $out;
|
||||
|
||||
mkdir $ConfDir or
|
||||
die "Failed to do mkdir $ConfDir\n";
|
||||
}
|
||||
|
||||
sub write_user_files ($) {
|
||||
my $block = shift;
|
||||
|
||||
my $name = $block->name;
|
||||
|
||||
if ($block->user_files) {
|
||||
my $raw = $block->user_files;
|
||||
|
||||
open my $in, '<', \$raw;
|
||||
|
||||
my @files;
|
||||
my ($fname, $body, $date);
|
||||
while (<$in>) {
|
||||
if (/>>> (\S+)(?:\s+(.+))?/) {
|
||||
if ($fname) {
|
||||
push @files, [$fname, $body, $date];
|
||||
}
|
||||
|
||||
$fname = $1;
|
||||
$date = $2;
|
||||
undef $body;
|
||||
} else {
|
||||
$body .= $_;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fname) {
|
||||
push @files, [$fname, $body, $date];
|
||||
}
|
||||
|
||||
for my $file (@files) {
|
||||
my ($fname, $body, $date) = @$file;
|
||||
#warn "write file $fname with content [$body]\n";
|
||||
|
||||
if (!defined $body) {
|
||||
$body = '';
|
||||
}
|
||||
|
||||
my $path;
|
||||
if ($fname !~ m{^/}) {
|
||||
$path = "$HtmlDir/$fname";
|
||||
|
||||
} else {
|
||||
$path = $fname;
|
||||
}
|
||||
|
||||
if ($path =~ /(.*)\//) {
|
||||
my $dir = $1;
|
||||
if (! -d $dir) {
|
||||
make_path($dir) or die "$name - Cannot create directory ", $dir;
|
||||
}
|
||||
}
|
||||
|
||||
open my $out, ">$path" or
|
||||
die "$name - Cannot open $path for writing: $!\n";
|
||||
print $out $body;
|
||||
close $out;
|
||||
|
||||
if ($date) {
|
||||
my $cmd = "touch -t '$date' $HtmlDir/$fname";
|
||||
system($cmd) == 0 or
|
||||
die "Failed to run shell command: $cmd\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub write_config_file ($$$) {
|
||||
my ($config, $http_config, $main_config) = @_;
|
||||
|
||||
if ($UseHup) {
|
||||
master_on(); # config reload is buggy when master is off
|
||||
|
||||
} elsif ($UseValgrind) {
|
||||
master_off();
|
||||
}
|
||||
|
||||
$http_config = expand_env_in_config($http_config);
|
||||
|
||||
if (!defined $config) {
|
||||
$config = '';
|
||||
}
|
||||
|
||||
if (!defined $http_config) {
|
||||
$http_config = '';
|
||||
}
|
||||
|
||||
if ($http_config =~ /\bpostpone_output\b/) {
|
||||
undef $PostponeOutput;
|
||||
}
|
||||
|
||||
if (defined $PostponeOutput) {
|
||||
if ($PostponeOutput !~ /^\d+$/) {
|
||||
die "Bad TEST_NGINX_POSTPOHNE_OUTPUT value: $PostponeOutput\n";
|
||||
}
|
||||
$http_config .= "\n postpone_output $PostponeOutput;\n";
|
||||
}
|
||||
|
||||
if (!defined $main_config) {
|
||||
$main_config = '';
|
||||
}
|
||||
|
||||
open my $out, ">$ConfFile" or
|
||||
die "Can't open $ConfFile for writing: $!\n";
|
||||
print $out <<_EOC_;
|
||||
worker_processes $Workers;
|
||||
daemon $DaemonEnabled;
|
||||
master_process $MasterProcessEnabled;
|
||||
error_log $ErrLogFile $LogLevel;
|
||||
pid $PidFile;
|
||||
env MOCKEAGAIN_VERBOSE;
|
||||
env MOCKEAGAIN_WRITE_TIMEOUT_PATTERN;
|
||||
env LD_PRELOAD;
|
||||
env DYLD_INSERT_LIBRARIES;
|
||||
|
||||
$main_config
|
||||
|
||||
http {
|
||||
access_log $AccLogFile;
|
||||
|
||||
default_type text/plain;
|
||||
keepalive_timeout 68;
|
||||
|
||||
$http_config
|
||||
|
||||
server {
|
||||
listen $ServerPort;
|
||||
server_name 'localhost';
|
||||
|
||||
client_max_body_size 30M;
|
||||
#client_body_buffer_size 4k;
|
||||
|
||||
# Begin preamble config...
|
||||
$ConfigPreamble
|
||||
# End preamble config...
|
||||
|
||||
# Begin test case config...
|
||||
$config
|
||||
# End test case config.
|
||||
|
||||
_EOC_
|
||||
|
||||
if (! $NoRootLocation) {
|
||||
print $out <<_EOC_;
|
||||
location / {
|
||||
root $HtmlDir;
|
||||
index index.html index.htm;
|
||||
}
|
||||
_EOC_
|
||||
}
|
||||
|
||||
print $out <<_EOC_;
|
||||
}
|
||||
}
|
||||
|
||||
events {
|
||||
worker_connections $WorkerConnections;
|
||||
_EOC_
|
||||
|
||||
if ($EventType) {
|
||||
print $out <<_EOC_;
|
||||
use $EventType;
|
||||
_EOC_
|
||||
}
|
||||
|
||||
print $out "}\n";
|
||||
|
||||
close $out;
|
||||
}
|
||||
|
||||
sub get_canon_version (@) {
|
||||
sprintf "%d.%03d%03d", $_[0], $_[1], $_[2];
|
||||
}
|
||||
|
||||
sub get_nginx_version () {
|
||||
my $out = `$NginxBinary -V 2>&1`;
|
||||
if (!defined $out || $? != 0) {
|
||||
warn "Failed to get the version of the Nginx in PATH.\n";
|
||||
}
|
||||
if ($out =~ m{(?:nginx|ngx_openresty)/(\d+)\.(\d+)\.(\d+)}s) {
|
||||
$NginxRawVersion = "$1.$2.$3";
|
||||
return get_canon_version($1, $2, $3);
|
||||
}
|
||||
warn "Failed to parse the output of \"nginx -V\": $out\n";
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub get_pid_from_pidfile ($) {
|
||||
my ($name) = @_;
|
||||
open my $in, $PidFile or
|
||||
bail_out("$name - Failed to open the pid file $PidFile for reading: $!");
|
||||
my $pid = do { local $/; <$in> };
|
||||
chomp $pid;
|
||||
#warn "Pid: $pid\n";
|
||||
close $in;
|
||||
return $pid;
|
||||
}
|
||||
|
||||
sub trim ($) {
|
||||
my $s = shift;
|
||||
return undef if !defined $s;
|
||||
$s =~ s/^\s+|\s+$//g;
|
||||
$s =~ s/\n/ /gs;
|
||||
$s =~ s/\s{2,}/ /gs;
|
||||
$s;
|
||||
}
|
||||
|
||||
sub show_all_chars ($) {
|
||||
my $s = shift;
|
||||
$s =~ s/\n/\\n/gs;
|
||||
$s =~ s/\r/\\r/gs;
|
||||
$s =~ s/\t/\\t/gs;
|
||||
$s;
|
||||
}
|
||||
|
||||
sub parse_headers ($) {
|
||||
my $s = shift;
|
||||
my %headers;
|
||||
open my $in, '<', \$s;
|
||||
while (<$in>) {
|
||||
s/^\s+|\s+$//g;
|
||||
my $neg = ($_ =~ s/^!\s*//);
|
||||
#warn "neg: $neg ($_)";
|
||||
if ($neg) {
|
||||
$headers{$_} = undef;
|
||||
} else {
|
||||
my ($key, $val) = split /\s*:\s*/, $_, 2;
|
||||
$headers{$key} = $val;
|
||||
}
|
||||
}
|
||||
close $in;
|
||||
return \%headers;
|
||||
}
|
||||
|
||||
sub expand_env_in_config ($) {
|
||||
my $config = shift;
|
||||
|
||||
if (!defined $config) {
|
||||
return;
|
||||
}
|
||||
|
||||
$config =~ s/\$(TEST_NGINX_[_A-Z0-9]+)/
|
||||
if (!defined $ENV{$1}) {
|
||||
bail_out "No environment $1 defined.\n";
|
||||
}
|
||||
$ENV{$1}/eg;
|
||||
|
||||
$config;
|
||||
}
|
||||
|
||||
sub check_if_missing_directives () {
|
||||
open my $in, $ErrLogFile or
|
||||
bail_out "check_if_missing_directives: Cannot open $ErrLogFile for reading: $!\n";
|
||||
|
||||
while (<$in>) {
|
||||
#warn $_;
|
||||
if (/\[emerg\] \S+?: unknown directive "([^"]+)"/) {
|
||||
#warn "MATCHED!!! $1";
|
||||
return $1;
|
||||
}
|
||||
}
|
||||
|
||||
close $in;
|
||||
|
||||
#warn "NOT MATCHED!!!";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub run_test ($) {
|
||||
my $block = shift;
|
||||
my $name = $block->name;
|
||||
|
||||
my $config = $block->config;
|
||||
|
||||
$config = expand_env_in_config($config);
|
||||
|
||||
my $dry_run = 0;
|
||||
my $should_restart = 1;
|
||||
my $should_reconfig = 1;
|
||||
|
||||
if (!defined $config) {
|
||||
if (!$NoNginxManager) {
|
||||
# Manager without config.
|
||||
if (!defined $PrevConfig) {
|
||||
bail_out("$name - No '--- config' section specified and could not get previous one. Use TEST_NGINX_NO_NGINX_MANAGER ?");
|
||||
die;
|
||||
}
|
||||
$should_reconfig = 0; # There is nothing to reconfig to.
|
||||
$should_restart = $ForceRestartOnTest;
|
||||
}
|
||||
# else: not manager without a config. This is not a problem at all.
|
||||
# setting these values to something meaningful but should not be used
|
||||
$should_restart = 0;
|
||||
$should_reconfig = 0;
|
||||
|
||||
} elsif ($NoNginxManager) {
|
||||
# One config but not manager: it's worth a warning.
|
||||
Test::Base::diag("NO_NGINX_MANAGER activated: config for $name ignored");
|
||||
# Like above: setting them to something meaningful just in case.
|
||||
$should_restart = 0;
|
||||
$should_reconfig = 0;
|
||||
|
||||
} else {
|
||||
# One config and manager. Restart only if forced to or if config
|
||||
# changed.
|
||||
if ((!defined $PrevConfig) || ($config ne $PrevConfig)) {
|
||||
$should_reconfig = 1;
|
||||
} else {
|
||||
$should_reconfig = 0;
|
||||
}
|
||||
if ($should_reconfig || $ForceRestartOnTest) {
|
||||
$should_restart = 1;
|
||||
} else {
|
||||
$should_restart = 0;
|
||||
}
|
||||
}
|
||||
|
||||
my $skip_nginx = $block->skip_nginx;
|
||||
my $skip_nginx2 = $block->skip_nginx2;
|
||||
my $skip_slave = $block->skip_slave;
|
||||
my ($tests_to_skip, $should_skip, $skip_reason);
|
||||
if (defined $skip_nginx) {
|
||||
if ($skip_nginx =~ m{
|
||||
^ \s* (\d+) \s* : \s*
|
||||
([<>]=?) \s* (\d+)\.(\d+)\.(\d+)
|
||||
(?: \s* : \s* (.*) )?
|
||||
\s*$}x) {
|
||||
$tests_to_skip = $1;
|
||||
my ($op, $ver1, $ver2, $ver3) = ($2, $3, $4, $5);
|
||||
$skip_reason = $6;
|
||||
#warn "$ver1 $ver2 $ver3";
|
||||
my $ver = get_canon_version($ver1, $ver2, $ver3);
|
||||
if ((!defined $NginxVersion and $op =~ /^</)
|
||||
or eval "$NginxVersion $op $ver")
|
||||
{
|
||||
$should_skip = 1;
|
||||
}
|
||||
} else {
|
||||
bail_out("$name - Invalid --- skip_nginx spec: " .
|
||||
$skip_nginx);
|
||||
die;
|
||||
}
|
||||
} elsif (defined $skip_nginx2) {
|
||||
if ($skip_nginx2 =~ m{
|
||||
^ \s* (\d+) \s* : \s*
|
||||
([<>]=?) \s* (\d+)\.(\d+)\.(\d+)
|
||||
\s* (or|and) \s*
|
||||
([<>]=?) \s* (\d+)\.(\d+)\.(\d+)
|
||||
(?: \s* : \s* (.*) )?
|
||||
\s*$}x) {
|
||||
$tests_to_skip = $1;
|
||||
my ($opa, $ver1a, $ver2a, $ver3a) = ($2, $3, $4, $5);
|
||||
my $opx = $6;
|
||||
my ($opb, $ver1b, $ver2b, $ver3b) = ($7, $8, $9, $10);
|
||||
$skip_reason = $11;
|
||||
my $vera = get_canon_version($ver1a, $ver2a, $ver3a);
|
||||
my $verb = get_canon_version($ver1b, $ver2b, $ver3b);
|
||||
|
||||
if ((!defined $NginxVersion)
|
||||
or (($opx eq "or") and (eval "$NginxVersion $opa $vera"
|
||||
or eval "$NginxVersion $opb $verb"))
|
||||
or (($opx eq "and") and (eval "$NginxVersion $opa $vera"
|
||||
and eval "$NginxVersion $opb $verb")))
|
||||
{
|
||||
$should_skip = 1;
|
||||
}
|
||||
} else {
|
||||
bail_out("$name - Invalid --- skip_nginx2 spec: " .
|
||||
$skip_nginx2);
|
||||
die;
|
||||
}
|
||||
} elsif (defined $skip_slave and defined $BuildSlaveName) {
|
||||
if ($skip_slave =~ m{
|
||||
^ \s* (\d+) \s* : \s*
|
||||
(\w+) \s* (?: (\w+) \s* )? (?: (\w+) \s* )?
|
||||
(?: \s* : \s* (.*) )? \s*$}x)
|
||||
{
|
||||
$tests_to_skip = $1;
|
||||
my ($slave1, $slave2, $slave3) = ($2, $3, $4);
|
||||
$skip_reason = $5;
|
||||
if ((defined $slave1 and $slave1 eq "all")
|
||||
or (defined $slave1 and $slave1 eq $BuildSlaveName)
|
||||
or (defined $slave2 and $slave2 eq $BuildSlaveName)
|
||||
or (defined $slave3 and $slave3 eq $BuildSlaveName)
|
||||
)
|
||||
{
|
||||
$should_skip = 1;
|
||||
}
|
||||
} else {
|
||||
bail_out("$name - Invalid --- skip_slave spec: " .
|
||||
$skip_slave);
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
if (!defined $skip_reason) {
|
||||
$skip_reason = "various reasons";
|
||||
}
|
||||
|
||||
my $todo_nginx = $block->todo_nginx;
|
||||
my ($should_todo, $todo_reason);
|
||||
if (defined $todo_nginx) {
|
||||
if ($todo_nginx =~ m{
|
||||
^ \s*
|
||||
([<>]=?) \s* (\d+)\.(\d+)\.(\d+)
|
||||
(?: \s* : \s* (.*) )?
|
||||
\s*$}x) {
|
||||
my ($op, $ver1, $ver2, $ver3) = ($1, $2, $3, $4);
|
||||
$todo_reason = $5;
|
||||
my $ver = get_canon_version($ver1, $ver2, $ver3);
|
||||
if ((!defined $NginxVersion and $op =~ /^</)
|
||||
or eval "$NginxVersion $op $ver")
|
||||
{
|
||||
$should_todo = 1;
|
||||
}
|
||||
} else {
|
||||
bail_out("$name - Invalid --- todo_nginx spec: " .
|
||||
$todo_nginx);
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
if (!defined $todo_reason) {
|
||||
$todo_reason = "various reasons";
|
||||
}
|
||||
|
||||
if (!$NoNginxManager && !$should_skip && $should_restart) {
|
||||
if ($should_reconfig) {
|
||||
$PrevConfig = $config;
|
||||
}
|
||||
my $nginx_is_running = 1;
|
||||
if (-f $PidFile) {
|
||||
my $pid = get_pid_from_pidfile($name);
|
||||
if (!defined $pid or $pid eq '') {
|
||||
undef $nginx_is_running;
|
||||
goto start_nginx;
|
||||
}
|
||||
|
||||
if (system("ps $pid > /dev/null") == 0) {
|
||||
#warn "found running nginx...";
|
||||
write_config_file($config, $block->http_config, $block->main_config);
|
||||
if (kill(SIGQUIT, $pid) == 0) { # send quit signal
|
||||
#warn("$name - Failed to send quit signal to the nginx process with PID $pid");
|
||||
}
|
||||
sleep 0.02;
|
||||
if (system("ps $pid > /dev/null") == 0) {
|
||||
#warn "killing with force...\n";
|
||||
kill(SIGKILL, $pid);
|
||||
sleep 0.02;
|
||||
}
|
||||
undef $nginx_is_running;
|
||||
} else {
|
||||
unlink $PidFile or
|
||||
die "Failed to remove pid file $PidFile\n";
|
||||
undef $nginx_is_running;
|
||||
}
|
||||
} else {
|
||||
undef $nginx_is_running;
|
||||
}
|
||||
|
||||
start_nginx:
|
||||
|
||||
unless ($nginx_is_running) {
|
||||
#system("killall -9 nginx");
|
||||
|
||||
#warn "*** Restarting the nginx server...\n";
|
||||
setup_server_root();
|
||||
write_user_files($block);
|
||||
write_config_file($config, $block->http_config, $block->main_config);
|
||||
#warn "nginx binary: $NginxBinary";
|
||||
if ( ! can_run($NginxBinary) ) {
|
||||
bail_out("$name - Cannot find the nginx executable in the PATH environment");
|
||||
die;
|
||||
}
|
||||
#if (system("nginx -p $ServRoot -c $ConfFile -t") != 0) {
|
||||
#Test::More::BAIL_OUT("$name - Invalid config file");
|
||||
#}
|
||||
#my $cmd = "nginx -p $ServRoot -c $ConfFile > /dev/null";
|
||||
if (!defined $NginxVersion) {
|
||||
$NginxVersion = $LatestNginxVersion;
|
||||
}
|
||||
|
||||
my $cmd;
|
||||
if ($NginxVersion >= 0.007053) {
|
||||
$cmd = "$NginxBinary -p $ServRoot/ -c $ConfFile > /dev/null";
|
||||
} else {
|
||||
$cmd = "$NginxBinary -c $ConfFile > /dev/null";
|
||||
}
|
||||
|
||||
if ($UseValgrind) {
|
||||
my $opts;
|
||||
|
||||
if ($UseValgrind =~ /^\d+$/) {
|
||||
$opts = "--tool=memcheck --leak-check=full";
|
||||
|
||||
} else {
|
||||
$opts = $UseValgrind;
|
||||
}
|
||||
|
||||
if (-f 'valgrind.suppress') {
|
||||
$cmd = "valgrind -q $opts --gen-suppressions=all --suppressions=valgrind.suppress $cmd";
|
||||
} else {
|
||||
$cmd = "valgrind -q $opts --gen-suppressions=all $cmd";
|
||||
}
|
||||
|
||||
warn "$name\n";
|
||||
#warn "$cmd\n";
|
||||
}
|
||||
|
||||
if ($Profiling || $UseValgrind) {
|
||||
my $pid = $ForkManager->start;
|
||||
if (!$pid) {
|
||||
# child process
|
||||
exec $cmd;
|
||||
|
||||
=begin cmt
|
||||
|
||||
if (system($cmd) != 0) {
|
||||
Test::More::BAIL_OUT("$name - Cannot start nginx using command \"$cmd\".");
|
||||
}
|
||||
|
||||
$ForkManager->finish; # terminate the child process
|
||||
|
||||
=end cmt
|
||||
|
||||
=cut
|
||||
|
||||
}
|
||||
#warn "sleeping";
|
||||
if ($TestNginxSleep) {
|
||||
sleep $TestNginxSleep;
|
||||
} else {
|
||||
sleep 1;
|
||||
}
|
||||
} else {
|
||||
if (system($cmd) != 0) {
|
||||
if ($ENV{TEST_NGINX_IGNORE_MISSING_DIRECTIVES} and
|
||||
my $directive = check_if_missing_directives())
|
||||
{
|
||||
$dry_run = $directive;
|
||||
|
||||
} else {
|
||||
bail_out("$name - Cannot start nginx using command \"$cmd\".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sleep 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
sleep 6;
|
||||
|
||||
if ($block->init) {
|
||||
eval $block->init;
|
||||
if ($@) {
|
||||
bail_out("$name - init failed: $@");
|
||||
}
|
||||
}
|
||||
|
||||
my $i = 0;
|
||||
while ($i++ < $RepeatEach) {
|
||||
#warn "Use hup: $UseHup, i: $i\n";
|
||||
|
||||
if ($UseHup && $i > 1) {
|
||||
my $pid = get_pid_from_pidfile($name);
|
||||
if (system("ps $pid > /dev/null") == 0) {
|
||||
if ($Verbose) {
|
||||
warn "sending HUP signal to $pid\n";
|
||||
}
|
||||
|
||||
if (kill(SIGHUP, $pid) == 0) { # send hup signal
|
||||
warn("$name - Failed to send HUP signal to the nginx process with PID $pid");
|
||||
}
|
||||
if ($TestNginxSleep) {
|
||||
sleep $TestNginxSleep;
|
||||
} else {
|
||||
sleep 0.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($should_skip) {
|
||||
SKIP: {
|
||||
Test::More::skip("$name - $skip_reason", $tests_to_skip);
|
||||
|
||||
$RunTestHelper->($block, $dry_run);
|
||||
}
|
||||
} elsif ($should_todo) {
|
||||
TODO: {
|
||||
local $TODO = "$name - $todo_reason";
|
||||
|
||||
$RunTestHelper->($block, $dry_run);
|
||||
}
|
||||
} else {
|
||||
$RunTestHelper->($block, $dry_run);
|
||||
}
|
||||
}
|
||||
|
||||
if (my $total_errlog = $ENV{TEST_NGINX_ERROR_LOG}) {
|
||||
my $errlog = $ErrLogFile;
|
||||
if (-s $errlog) {
|
||||
open my $out, ">>$total_errlog" or
|
||||
die "Failed to append test case title to $total_errlog: $!\n";
|
||||
print $out "\n=== $0 $name\n";
|
||||
close $out;
|
||||
system("cat $errlog >> $total_errlog") == 0 or
|
||||
die "Failed to append $errlog to $total_errlog. Abort.\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($Profiling || $UseValgrind) {
|
||||
#warn "Found quit...";
|
||||
if (-f $PidFile) {
|
||||
#warn "found pid file...";
|
||||
my $pid = get_pid_from_pidfile($name);
|
||||
my $i = 0;
|
||||
retry:
|
||||
if (system("ps $pid > /dev/null") == 0) {
|
||||
write_config_file($config, $block->http_config, $block->main_config);
|
||||
|
||||
if ($Verbose) {
|
||||
warn "sending QUIT signal to $pid\n";
|
||||
}
|
||||
|
||||
if (kill(SIGQUIT, $pid) == 0) { # send quit signal
|
||||
warn("$name - Failed to send quit signal to the nginx process with PID $pid");
|
||||
}
|
||||
|
||||
if ($TestNginxSleep) {
|
||||
sleep $TestNginxSleep;
|
||||
} else {
|
||||
sleep 0.1;
|
||||
}
|
||||
|
||||
if (-f $PidFile) {
|
||||
if ($i++ < 5) {
|
||||
if ($Verbose) {
|
||||
warn "nginx not quitted, retrying...\n";
|
||||
}
|
||||
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if ($Verbose) {
|
||||
warn "sending KILL signal to $pid\n";
|
||||
}
|
||||
|
||||
kill(SIGKILL, $pid);
|
||||
sleep 0.02;
|
||||
|
||||
} else {
|
||||
#warn "nginx killed";
|
||||
}
|
||||
|
||||
} else {
|
||||
unlink $PidFile or
|
||||
die "Failed to remove pid file $PidFile\n";
|
||||
}
|
||||
} else {
|
||||
#warn "pid file not found";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
END {
|
||||
if ($UseValgrind || !$ENV{TEST_NGINX_NO_CLEAN}) {
|
||||
local $?; # to avoid confusing Test::Builder::_ending
|
||||
if (-f $PidFile) {
|
||||
my $pid = get_pid_from_pidfile('');
|
||||
if (!$pid) {
|
||||
die "No pid found.";
|
||||
}
|
||||
if (system("ps $pid > /dev/null") == 0) {
|
||||
if (kill(SIGQUIT, $pid) == 0) { # send quit signal
|
||||
#warn("Failed to send quit signal to the nginx process with PID $pid");
|
||||
}
|
||||
if ($TestNginxSleep) {
|
||||
sleep $TestNginxSleep;
|
||||
} else {
|
||||
sleep 0.02;
|
||||
}
|
||||
if (system("ps $pid > /dev/null") == 0) {
|
||||
#warn "killing with force...\n";
|
||||
kill(SIGKILL, $pid);
|
||||
sleep 0.02;
|
||||
}
|
||||
} else {
|
||||
unlink $PidFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# check if we can run some command
|
||||
sub can_run {
|
||||
my ($cmd) = @_;
|
||||
|
||||
#warn "can run: @_\n";
|
||||
my $_cmd = $cmd;
|
||||
return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd));
|
||||
|
||||
for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') {
|
||||
next if $dir eq '';
|
||||
my $abs = File::Spec->catfile($dir, $_[0]);
|
||||
return $abs if (-x $abs or $abs = MM->maybe_command($abs));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
1;
|
@ -0,0 +1,13 @@
|
||||
default: server client
|
||||
|
||||
server: http11_parser.rl ragel_http_server.c
|
||||
ragel -G2 http11_parser.rl
|
||||
gcc -g -Wall ragel_http_server.c http11_parser.c -o server
|
||||
|
||||
|
||||
client: http11_response.rl ragel_http_client.c
|
||||
ragel -G2 http11_response.rl
|
||||
gcc -g -Wall ragel_http_client.c http11_response.c -o client
|
||||
|
||||
clean:
|
||||
rm client server
|
@ -0,0 +1,534 @@
|
||||
/**
|
||||
* Copyright (c) 2005 Zed A. Shaw
|
||||
* You can redistribute it and/or modify it under the same terms as Ruby.
|
||||
*/
|
||||
#include "ruby.h"
|
||||
#include "ext_help.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "http11_parser.h"
|
||||
|
||||
#ifndef RSTRING_PTR
|
||||
#define RSTRING_PTR(s) (RSTRING(s)->ptr)
|
||||
#endif
|
||||
#ifndef RSTRING_LEN
|
||||
#define RSTRING_LEN(s) (RSTRING(s)->len)
|
||||
#endif
|
||||
|
||||
#ifndef RSTRING_PTR
|
||||
#define RSTRING_PTR(s) (RSTRING(s)->ptr)
|
||||
#endif
|
||||
#ifndef RSTRING_LEN
|
||||
#define RSTRING_LEN(s) (RSTRING(s)->len)
|
||||
#endif
|
||||
|
||||
static VALUE mMongrel;
|
||||
static VALUE cHttpParser;
|
||||
static VALUE eHttpParserError;
|
||||
|
||||
#define id_handler_map rb_intern("@handler_map")
|
||||
#define id_http_body rb_intern("@http_body")
|
||||
#define HTTP_PREFIX "HTTP_"
|
||||
#define HTTP_PREFIX_LEN (sizeof(HTTP_PREFIX) - 1)
|
||||
|
||||
static VALUE global_request_method;
|
||||
static VALUE global_request_uri;
|
||||
static VALUE global_fragment;
|
||||
static VALUE global_query_string;
|
||||
static VALUE global_http_version;
|
||||
static VALUE global_content_length;
|
||||
static VALUE global_http_content_length;
|
||||
static VALUE global_request_path;
|
||||
static VALUE global_content_type;
|
||||
static VALUE global_http_content_type;
|
||||
static VALUE global_gateway_interface;
|
||||
static VALUE global_gateway_interface_value;
|
||||
static VALUE global_server_name;
|
||||
static VALUE global_server_port;
|
||||
static VALUE global_server_protocol;
|
||||
static VALUE global_server_protocol_value;
|
||||
static VALUE global_http_host;
|
||||
static VALUE global_mongrel_version;
|
||||
static VALUE global_server_software;
|
||||
static VALUE global_port_80;
|
||||
|
||||
#define TRIE_INCREASE 30
|
||||
|
||||
/** Defines common length and error messages for input length validation. */
|
||||
#define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " # length " allowed length."
|
||||
|
||||
/** Validates the max length of given input and throws an HttpParserError exception if over. */
|
||||
#define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR); }
|
||||
|
||||
/** Defines global strings in the init method. */
|
||||
#define DEF_GLOBAL(N, val) global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N)
|
||||
|
||||
|
||||
/* Defines the maximum allowed lengths for various input elements.*/
|
||||
DEF_MAX_LENGTH(FIELD_NAME, 256);
|
||||
DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
|
||||
DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
|
||||
DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
|
||||
DEF_MAX_LENGTH(REQUEST_PATH, 1024);
|
||||
DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
|
||||
DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
|
||||
|
||||
struct common_field {
|
||||
const signed long len;
|
||||
const char *name;
|
||||
VALUE value;
|
||||
};
|
||||
|
||||
/*
|
||||
* A list of common HTTP headers we expect to receive.
|
||||
* This allows us to avoid repeatedly creating identical string
|
||||
* objects to be used with rb_hash_aset().
|
||||
*/
|
||||
static struct common_field common_http_fields[] = {
|
||||
# define f(N) { (sizeof(N) - 1), N, Qnil }
|
||||
f("ACCEPT"),
|
||||
f("ACCEPT_CHARSET"),
|
||||
f("ACCEPT_ENCODING"),
|
||||
f("ACCEPT_LANGUAGE"),
|
||||
f("ALLOW"),
|
||||
f("AUTHORIZATION"),
|
||||
f("CACHE_CONTROL"),
|
||||
f("CONNECTION"),
|
||||
f("CONTENT_ENCODING"),
|
||||
f("CONTENT_LENGTH"),
|
||||
f("CONTENT_TYPE"),
|
||||
f("COOKIE"),
|
||||
f("DATE"),
|
||||
f("EXPECT"),
|
||||
f("FROM"),
|
||||
f("HOST"),
|
||||
f("IF_MATCH"),
|
||||
f("IF_MODIFIED_SINCE"),
|
||||
f("IF_NONE_MATCH"),
|
||||
f("IF_RANGE"),
|
||||
f("IF_UNMODIFIED_SINCE"),
|
||||
f("KEEP_ALIVE"), /* Firefox sends this */
|
||||
f("MAX_FORWARDS"),
|
||||
f("PRAGMA"),
|
||||
f("PROXY_AUTHORIZATION"),
|
||||
f("RANGE"),
|
||||
f("REFERER"),
|
||||
f("TE"),
|
||||
f("TRAILER"),
|
||||
f("TRANSFER_ENCODING"),
|
||||
f("UPGRADE"),
|
||||
f("USER_AGENT"),
|
||||
f("VIA"),
|
||||
f("X_FORWARDED_FOR"), /* common for proxies */
|
||||
f("X_REAL_IP"), /* common for proxies */
|
||||
f("WARNING")
|
||||
# undef f
|
||||
};
|
||||
|
||||
/*
|
||||
* qsort(3) and bsearch(3) improve average performance slightly, but may
|
||||
* not be worth it for lack of portability to certain platforms...
|
||||
*/
|
||||
#if defined(HAVE_QSORT_BSEARCH)
|
||||
/* sort by length, then by name if there's a tie */
|
||||
static int common_field_cmp(const void *a, const void *b)
|
||||
{
|
||||
struct common_field *cfa = (struct common_field *)a;
|
||||
struct common_field *cfb = (struct common_field *)b;
|
||||
signed long diff = cfa->len - cfb->len;
|
||||
return diff ? diff : memcmp(cfa->name, cfb->name, cfa->len);
|
||||
}
|
||||
#endif /* HAVE_QSORT_BSEARCH */
|
||||
|
||||
static void init_common_fields(void)
|
||||
{
|
||||
int i;
|
||||
struct common_field *cf = common_http_fields;
|
||||
char tmp[256]; /* MAX_FIELD_NAME_LENGTH */
|
||||
memcpy(tmp, HTTP_PREFIX, HTTP_PREFIX_LEN);
|
||||
|
||||
for(i = 0; i < ARRAY_SIZE(common_http_fields); cf++, i++) {
|
||||
memcpy(tmp + HTTP_PREFIX_LEN, cf->name, cf->len + 1);
|
||||
cf->value = rb_obj_freeze(rb_str_new(tmp, HTTP_PREFIX_LEN + cf->len));
|
||||
rb_global_variable(&cf->value);
|
||||
}
|
||||
|
||||
#if defined(HAVE_QSORT_BSEARCH)
|
||||
qsort(common_http_fields,
|
||||
ARRAY_SIZE(common_http_fields),
|
||||
sizeof(struct common_field),
|
||||
common_field_cmp);
|
||||
#endif /* HAVE_QSORT_BSEARCH */
|
||||
}
|
||||
|
||||
static VALUE find_common_field_value(const char *field, size_t flen)
|
||||
{
|
||||
#if defined(HAVE_QSORT_BSEARCH)
|
||||
struct common_field key;
|
||||
struct common_field *found;
|
||||
key.name = field;
|
||||
key.len = (signed long)flen;
|
||||
found = (struct common_field *)bsearch(&key, common_http_fields,
|
||||
ARRAY_SIZE(common_http_fields),
|
||||
sizeof(struct common_field),
|
||||
common_field_cmp);
|
||||
return found ? found->value : Qnil;
|
||||
#else /* !HAVE_QSORT_BSEARCH */
|
||||
int i;
|
||||
struct common_field *cf = common_http_fields;
|
||||
for(i = 0; i < ARRAY_SIZE(common_http_fields); i++, cf++) {
|
||||
if (cf->len == flen && !memcmp(cf->name, field, flen))
|
||||
return cf->value;
|
||||
}
|
||||
return Qnil;
|
||||
#endif /* !HAVE_QSORT_BSEARCH */
|
||||
}
|
||||
|
||||
void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
|
||||
{
|
||||
VALUE req = (VALUE)data;
|
||||
VALUE v = Qnil;
|
||||
VALUE f = Qnil;
|
||||
|
||||
VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
|
||||
VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
|
||||
|
||||
v = rb_str_new(value, vlen);
|
||||
|
||||
f = find_common_field_value(field, flen);
|
||||
|
||||
if (f == Qnil) {
|
||||
/*
|
||||
* We got a strange header that we don't have a memoized value for.
|
||||
* Fallback to creating a new string to use as a hash key.
|
||||
*
|
||||
* using rb_str_new(NULL, len) here is faster than rb_str_buf_new(len)
|
||||
* in my testing, because: there's no minimum allocation length (and
|
||||
* no check for it, either), RSTRING_LEN(f) does not need to be
|
||||
* written twice, and and RSTRING_PTR(f) will already be
|
||||
* null-terminated for us.
|
||||
*/
|
||||
f = rb_str_new(NULL, HTTP_PREFIX_LEN + flen);
|
||||
memcpy(RSTRING_PTR(f), HTTP_PREFIX, HTTP_PREFIX_LEN);
|
||||
memcpy(RSTRING_PTR(f) + HTTP_PREFIX_LEN, field, flen);
|
||||
assert(*(RSTRING_PTR(f) + RSTRING_LEN(f)) == '\0'); /* paranoia */
|
||||
/* fprintf(stderr, "UNKNOWN HEADER <%s>\n", RSTRING_PTR(f)); */
|
||||
}
|
||||
|
||||
rb_hash_aset(req, f, v);
|
||||
}
|
||||
|
||||
void request_method(void *data, const char *at, size_t length)
|
||||
{
|
||||
VALUE req = (VALUE)data;
|
||||
VALUE val = Qnil;
|
||||
|
||||
val = rb_str_new(at, length);
|
||||
rb_hash_aset(req, global_request_method, val);
|
||||
}
|
||||
|
||||
void request_uri(void *data, const char *at, size_t length)
|
||||
{
|
||||
VALUE req = (VALUE)data;
|
||||
VALUE val = Qnil;
|
||||
|
||||
VALIDATE_MAX_LENGTH(length, REQUEST_URI);
|
||||
|
||||
val = rb_str_new(at, length);
|
||||
rb_hash_aset(req, global_request_uri, val);
|
||||
}
|
||||
|
||||
void fragment(void *data, const char *at, size_t length)
|
||||
{
|
||||
VALUE req = (VALUE)data;
|
||||
VALUE val = Qnil;
|
||||
|
||||
VALIDATE_MAX_LENGTH(length, FRAGMENT);
|
||||
|
||||
val = rb_str_new(at, length);
|
||||
rb_hash_aset(req, global_fragment, val);
|
||||
}
|
||||
|
||||
void request_path(void *data, const char *at, size_t length)
|
||||
{
|
||||
VALUE req = (VALUE)data;
|
||||
VALUE val = Qnil;
|
||||
|
||||
VALIDATE_MAX_LENGTH(length, REQUEST_PATH);
|
||||
|
||||
val = rb_str_new(at, length);
|
||||
rb_hash_aset(req, global_request_path, val);
|
||||
}
|
||||
|
||||
void query_string(void *data, const char *at, size_t length)
|
||||
{
|
||||
VALUE req = (VALUE)data;
|
||||
VALUE val = Qnil;
|
||||
|
||||
VALIDATE_MAX_LENGTH(length, QUERY_STRING);
|
||||
|
||||
val = rb_str_new(at, length);
|
||||
rb_hash_aset(req, global_query_string, val);
|
||||
}
|
||||
|
||||
void http_version(void *data, const char *at, size_t length)
|
||||
{
|
||||
VALUE req = (VALUE)data;
|
||||
VALUE val = rb_str_new(at, length);
|
||||
rb_hash_aset(req, global_http_version, val);
|
||||
}
|
||||
|
||||
/** Finalizes the request header to have a bunch of stuff that's
|
||||
needed. */
|
||||
|
||||
void header_done(void *data, const char *at, size_t length)
|
||||
{
|
||||
VALUE req = (VALUE)data;
|
||||
VALUE temp = Qnil;
|
||||
VALUE ctype = Qnil;
|
||||
VALUE clen = Qnil;
|
||||
char *colon = NULL;
|
||||
|
||||
clen = rb_hash_aref(req, global_http_content_length);
|
||||
if(clen != Qnil) {
|
||||
rb_hash_aset(req, global_content_length, clen);
|
||||
}
|
||||
|
||||
ctype = rb_hash_aref(req, global_http_content_type);
|
||||
if(ctype != Qnil) {
|
||||
rb_hash_aset(req, global_content_type, ctype);
|
||||
}
|
||||
|
||||
rb_hash_aset(req, global_gateway_interface, global_gateway_interface_value);
|
||||
if((temp = rb_hash_aref(req, global_http_host)) != Qnil) {
|
||||
colon = memchr(RSTRING_PTR(temp), ':', RSTRING_LEN(temp));
|
||||
if(colon != NULL) {
|
||||
rb_hash_aset(req, global_server_name, rb_str_substr(temp, 0, colon - RSTRING_PTR(temp)));
|
||||
rb_hash_aset(req, global_server_port,
|
||||
rb_str_substr(temp, colon - RSTRING_PTR(temp)+1,
|
||||
RSTRING_LEN(temp)));
|
||||
} else {
|
||||
rb_hash_aset(req, global_server_name, temp);
|
||||
rb_hash_aset(req, global_server_port, global_port_80);
|
||||
}
|
||||
}
|
||||
|
||||
/* grab the initial body and stuff it into an ivar */
|
||||
rb_ivar_set(req, id_http_body, rb_str_new(at, length));
|
||||
rb_hash_aset(req, global_server_protocol, global_server_protocol_value);
|
||||
rb_hash_aset(req, global_server_software, global_mongrel_version);
|
||||
}
|
||||
|
||||
|
||||
void HttpParser_free(void *data) {
|
||||
TRACE();
|
||||
|
||||
if(data) {
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VALUE HttpParser_alloc(VALUE klass)
|
||||
{
|
||||
VALUE obj;
|
||||
http_parser *hp = ALLOC_N(http_parser, 1);
|
||||
TRACE();
|
||||
hp->http_field = http_field;
|
||||
hp->request_method = request_method;
|
||||
hp->request_uri = request_uri;
|
||||
hp->fragment = fragment;
|
||||
hp->request_path = request_path;
|
||||
hp->query_string = query_string;
|
||||
hp->http_version = http_version;
|
||||
hp->header_done = header_done;
|
||||
http_parser_init(hp);
|
||||
|
||||
obj = Data_Wrap_Struct(klass, NULL, HttpParser_free, hp);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* call-seq:
|
||||
* parser.new -> parser
|
||||
*
|
||||
* Creates a new parser.
|
||||
*/
|
||||
VALUE HttpParser_init(VALUE self)
|
||||
{
|
||||
http_parser *http = NULL;
|
||||
DATA_GET(self, http_parser, http);
|
||||
http_parser_init(http);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* call-seq:
|
||||
* parser.reset -> nil
|
||||
*
|
||||
* Resets the parser to it's initial state so that you can reuse it
|
||||
* rather than making new ones.
|
||||
*/
|
||||
VALUE HttpParser_reset(VALUE self)
|
||||
{
|
||||
http_parser *http = NULL;
|
||||
DATA_GET(self, http_parser, http);
|
||||
http_parser_init(http);
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* call-seq:
|
||||
* parser.finish -> true/false
|
||||
*
|
||||
* Finishes a parser early which could put in a "good" or bad state.
|
||||
* You should call reset after finish it or bad things will happen.
|
||||
*/
|
||||
VALUE HttpParser_finish(VALUE self)
|
||||
{
|
||||
http_parser *http = NULL;
|
||||
DATA_GET(self, http_parser, http);
|
||||
http_parser_finish(http);
|
||||
|
||||
return http_parser_is_finished(http) ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* call-seq:
|
||||
* parser.execute(req_hash, data, start) -> Integer
|
||||
*
|
||||
* Takes a Hash and a String of data, parses the String of data filling in the Hash
|
||||
* returning an Integer to indicate how much of the data has been read. No matter
|
||||
* what the return value, you should call HttpParser#finished? and HttpParser#error?
|
||||
* to figure out if it's done parsing or there was an error.
|
||||
*
|
||||
* This function now throws an exception when there is a parsing error. This makes
|
||||
* the logic for working with the parser much easier. You can still test for an
|
||||
* error, but now you need to wrap the parser with an exception handling block.
|
||||
*
|
||||
* The third argument allows for parsing a partial request and then continuing
|
||||
* the parsing from that position. It needs all of the original data as well
|
||||
* so you have to append to the data buffer as you read.
|
||||
*/
|
||||
VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
|
||||
{
|
||||
http_parser *http = NULL;
|
||||
int from = 0;
|
||||
char *dptr = NULL;
|
||||
long dlen = 0;
|
||||
|
||||
DATA_GET(self, http_parser, http);
|
||||
|
||||
from = FIX2INT(start);
|
||||
dptr = RSTRING_PTR(data);
|
||||
dlen = RSTRING_LEN(data);
|
||||
|
||||
if(from >= dlen) {
|
||||
rb_raise(eHttpParserError, "Requested start is after data buffer end.");
|
||||
} else {
|
||||
http->data = (void *)req_hash;
|
||||
http_parser_execute(http, dptr, dlen, from);
|
||||
|
||||
VALIDATE_MAX_LENGTH(http_parser_nread(http), HEADER);
|
||||
|
||||
if(http_parser_has_error(http)) {
|
||||
rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
|
||||
} else {
|
||||
return INT2FIX(http_parser_nread(http));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* call-seq:
|
||||
* parser.error? -> true/false
|
||||
*
|
||||
* Tells you whether the parser is in an error state.
|
||||
*/
|
||||
VALUE HttpParser_has_error(VALUE self)
|
||||
{
|
||||
http_parser *http = NULL;
|
||||
DATA_GET(self, http_parser, http);
|
||||
|
||||
return http_parser_has_error(http) ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* call-seq:
|
||||
* parser.finished? -> true/false
|
||||
*
|
||||
* Tells you whether the parser is finished or not and in a good state.
|
||||
*/
|
||||
VALUE HttpParser_is_finished(VALUE self)
|
||||
{
|
||||
http_parser *http = NULL;
|
||||
DATA_GET(self, http_parser, http);
|
||||
|
||||
return http_parser_is_finished(http) ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* call-seq:
|
||||
* parser.nread -> Integer
|
||||
*
|
||||
* Returns the amount of data processed so far during this processing cycle. It is
|
||||
* set to 0 on initialize or reset calls and is incremented each time execute is called.
|
||||
*/
|
||||
VALUE HttpParser_nread(VALUE self)
|
||||
{
|
||||
http_parser *http = NULL;
|
||||
DATA_GET(self, http_parser, http);
|
||||
|
||||
return INT2FIX(http->nread);
|
||||
}
|
||||
|
||||
void Init_http11()
|
||||
{
|
||||
|
||||
mMongrel = rb_define_module("Mongrel");
|
||||
|
||||
DEF_GLOBAL(request_method, "REQUEST_METHOD");
|
||||
DEF_GLOBAL(request_uri, "REQUEST_URI");
|
||||
DEF_GLOBAL(fragment, "FRAGMENT");
|
||||
DEF_GLOBAL(query_string, "QUERY_STRING");
|
||||
DEF_GLOBAL(http_version, "HTTP_VERSION");
|
||||
DEF_GLOBAL(request_path, "REQUEST_PATH");
|
||||
DEF_GLOBAL(content_length, "CONTENT_LENGTH");
|
||||
DEF_GLOBAL(http_content_length, "HTTP_CONTENT_LENGTH");
|
||||
DEF_GLOBAL(content_type, "CONTENT_TYPE");
|
||||
DEF_GLOBAL(http_content_type, "HTTP_CONTENT_TYPE");
|
||||
DEF_GLOBAL(gateway_interface, "GATEWAY_INTERFACE");
|
||||
DEF_GLOBAL(gateway_interface_value, "CGI/1.2");
|
||||
DEF_GLOBAL(server_name, "SERVER_NAME");
|
||||
DEF_GLOBAL(server_port, "SERVER_PORT");
|
||||
DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
|
||||
DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
|
||||
DEF_GLOBAL(http_host, "HTTP_HOST");
|
||||
DEF_GLOBAL(mongrel_version, "Mongrel 1.2.0.pre2"); /* XXX Why is this defined here? */
|
||||
DEF_GLOBAL(server_software, "SERVER_SOFTWARE");
|
||||
DEF_GLOBAL(port_80, "80");
|
||||
|
||||
eHttpParserError = rb_define_class_under(mMongrel, "HttpParserError", rb_eIOError);
|
||||
|
||||
cHttpParser = rb_define_class_under(mMongrel, "HttpParser", rb_cObject);
|
||||
rb_define_alloc_func(cHttpParser, HttpParser_alloc);
|
||||
rb_define_method(cHttpParser, "initialize", HttpParser_init,0);
|
||||
rb_define_method(cHttpParser, "reset", HttpParser_reset,0);
|
||||
rb_define_method(cHttpParser, "finish", HttpParser_finish,0);
|
||||
rb_define_method(cHttpParser, "execute", HttpParser_execute,3);
|
||||
rb_define_method(cHttpParser, "error?", HttpParser_has_error,0);
|
||||
rb_define_method(cHttpParser, "finished?", HttpParser_is_finished,0);
|
||||
rb_define_method(cHttpParser, "nread", HttpParser_nread,0);
|
||||
init_common_fields();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2005 Zed A. Shaw
|
||||
* You can redistribute it and/or modify it under the same terms as Ruby.
|
||||
*/
|
||||
|
||||
#ifndef http11_parser_h
|
||||
#define http11_parser_h
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
|
||||
typedef void (*element_cb)(void *data, const char *at, size_t length);
|
||||
typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen);
|
||||
|
||||
typedef struct http_parser {
|
||||
int cs;
|
||||
size_t body_start;
|
||||
int content_len;
|
||||
size_t nread;
|
||||
size_t mark;
|
||||
size_t field_start;
|
||||
size_t field_len;
|
||||
size_t query_start;
|
||||
|
||||
void *data;
|
||||
|
||||
field_cb http_field;
|
||||
element_cb request_method;
|
||||
element_cb request_uri;
|
||||
element_cb fragment;
|
||||
element_cb request_path;
|
||||
element_cb query_string;
|
||||
element_cb http_version;
|
||||
element_cb header_done;
|
||||
|
||||
} http_parser;
|
||||
|
||||
int http_parser_init(http_parser *parser);
|
||||
int http_parser_finish(http_parser *parser);
|
||||
size_t http_parser_execute(http_parser *parser, const char *data, size_t len, size_t off);
|
||||
int http_parser_has_error(http_parser *parser);
|
||||
int http_parser_is_finished(http_parser *parser);
|
||||
|
||||
#define http_parser_nread(parser) (parser)->nread
|
||||
|
||||
#endif
|
@ -0,0 +1,141 @@
|
||||
/**
|
||||
* Copyright (c) 2005 Zed A. Shaw
|
||||
* You can redistribute it and/or modify it under the same terms as Ruby.
|
||||
*/
|
||||
#include "http11_parser.h"
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define LEN(AT, FPC) (FPC - buffer - parser->AT)
|
||||
#define MARK(M,FPC) (parser->M = (FPC) - buffer)
|
||||
#define PTR_TO(F) (buffer + parser->F)
|
||||
|
||||
/** Machine **/
|
||||
|
||||
%%{
|
||||
|
||||
machine http_parser;
|
||||
|
||||
action mark {MARK(mark, fpc); }
|
||||
|
||||
|
||||
action start_field { MARK(field_start, fpc); }
|
||||
action write_field {
|
||||
parser->field_len = LEN(field_start, fpc);
|
||||
}
|
||||
|
||||
action start_value { MARK(mark, fpc); }
|
||||
action write_value {
|
||||
if(parser->http_field != NULL) {
|
||||
parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
|
||||
}
|
||||
}
|
||||
action request_method {
|
||||
if(parser->request_method != NULL)
|
||||
parser->request_method(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
||||
}
|
||||
action request_uri {
|
||||
if(parser->request_uri != NULL)
|
||||
parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
||||
}
|
||||
action fragment {
|
||||
if(parser->fragment != NULL)
|
||||
parser->fragment(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
||||
}
|
||||
|
||||
action start_query {MARK(query_start, fpc); }
|
||||
action query_string {
|
||||
if(parser->query_string != NULL)
|
||||
parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, fpc));
|
||||
}
|
||||
|
||||
action http_version {
|
||||
if(parser->http_version != NULL)
|
||||
parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
||||
}
|
||||
|
||||
action request_path {
|
||||
if(parser->request_path != NULL)
|
||||
parser->request_path(parser->data, PTR_TO(mark), LEN(mark,fpc));
|
||||
}
|
||||
|
||||
action done {
|
||||
parser->body_start = fpc - buffer + 1;
|
||||
if(parser->header_done != NULL)
|
||||
parser->header_done(parser->data, fpc + 1, pe - fpc - 1);
|
||||
fbreak;
|
||||
}
|
||||
|
||||
include http_parser_common "http11_parser_common.rl";
|
||||
|
||||
}%%
|
||||
|
||||
/** Data **/
|
||||
%% write data;
|
||||
|
||||
int http_parser_init(http_parser *parser) {
|
||||
int cs = 0;
|
||||
%% write init;
|
||||
parser->cs = cs;
|
||||
parser->body_start = 0;
|
||||
parser->content_len = 0;
|
||||
parser->mark = 0;
|
||||
parser->nread = 0;
|
||||
parser->field_len = 0;
|
||||
parser->field_start = 0;
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/** exec **/
|
||||
size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off) {
|
||||
const char *p, *pe;
|
||||
int cs = parser->cs;
|
||||
|
||||
assert(off <= len && "offset past end of buffer");
|
||||
|
||||
p = buffer+off;
|
||||
pe = buffer+len;
|
||||
|
||||
/* assert(*pe == '\0' && "pointer does not end on NUL"); */
|
||||
assert(pe - p == len - off && "pointers aren't same distance");
|
||||
|
||||
%% write exec;
|
||||
|
||||
if (!http_parser_has_error(parser))
|
||||
parser->cs = cs;
|
||||
parser->nread += p - (buffer + off);
|
||||
|
||||
assert(p <= pe && "buffer overflow after parsing execute");
|
||||
assert(parser->nread <= len && "nread longer than length");
|
||||
assert(parser->body_start <= len && "body starts after buffer end");
|
||||
assert(parser->mark < len && "mark is after buffer end");
|
||||
assert(parser->field_len <= len && "field has length longer than whole buffer");
|
||||
assert(parser->field_start < len && "field starts after buffer end");
|
||||
|
||||
return(parser->nread);
|
||||
}
|
||||
|
||||
int http_parser_finish(http_parser *parser)
|
||||
{
|
||||
if (http_parser_has_error(parser) ) {
|
||||
return -1;
|
||||
} else if (http_parser_is_finished(parser) ) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int http_parser_has_error(http_parser *parser) {
|
||||
return parser->cs == http_parser_error;
|
||||
}
|
||||
|
||||
int http_parser_is_finished(http_parser *parser) {
|
||||
return parser->cs >= http_parser_first_final;
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
%%{
|
||||
|
||||
machine http_parser_common;
|
||||
|
||||
#### HTTP PROTOCOL GRAMMAR
|
||||
# line endings
|
||||
CRLF = "\r\n";
|
||||
|
||||
# character types
|
||||
CTL = (cntrl | 127);
|
||||
safe = ("$" | "-" | "_" | ".");
|
||||
extra = ("!" | "*" | "'" | "(" | ")" | ",");
|
||||
reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+");
|
||||
unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">");
|
||||
national = any -- (alpha | digit | reserved | extra | safe | unsafe);
|
||||
unreserved = (alpha | digit | safe | extra | national);
|
||||
escape = ("%" xdigit xdigit);
|
||||
uchar = (unreserved | escape);
|
||||
pchar = (uchar | ":" | "@" | "&" | "=" | "+");
|
||||
tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
|
||||
|
||||
# elements
|
||||
token = (ascii -- (CTL | tspecials));
|
||||
|
||||
# URI schemes and absolute paths
|
||||
scheme = ( alpha | digit | "+" | "-" | "." )* ;
|
||||
absolute_uri = (scheme ":" (uchar | reserved )*);
|
||||
|
||||
path = ( pchar+ ( "/" pchar* )* ) ;
|
||||
query = ( uchar | reserved )* %query_string ;
|
||||
param = ( pchar | "/" )* ;
|
||||
params = ( param ( ";" param )* ) ;
|
||||
rel_path = ( path? %request_path (";" params)? ) ("?" %start_query query)?;
|
||||
absolute_path = ( "/"+ rel_path );
|
||||
|
||||
Request_URI = ( "*" | absolute_uri | absolute_path ) >mark %request_uri;
|
||||
Fragment = ( uchar | reserved )* >mark %fragment;
|
||||
Method = ( upper | digit | safe ){1,20} >mark %request_method;
|
||||
|
||||
http_number = ( digit+ "." digit+ ) ;
|
||||
HTTP_Version = ( "HTTP/" http_number ) >mark %http_version ;
|
||||
Request_Line = ( Method " " Request_URI ("#" Fragment){0,1} " " HTTP_Version CRLF ) ;
|
||||
|
||||
#field_name = ( token -- ":" )+ >start_field $snake_upcase_field %write_field;
|
||||
field_name = ( token -- ":" )+ >start_field %write_field;
|
||||
|
||||
field_value = any* >start_value %write_value;
|
||||
|
||||
message_header = field_name ":" " "* field_value :> CRLF;
|
||||
|
||||
Request = Request_Line ( message_header )* ( CRLF @done );
|
||||
|
||||
main := Request;
|
||||
|
||||
}%%
|
@ -0,0 +1,419 @@
|
||||
|
||||
#line 1 "http11_response.rl"
|
||||
|
||||
#include "http11_response.h"
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define LEN(AT, FPC) (FPC - buffer - parser->AT)
|
||||
#define MARK(M,FPC) (parser->M = (FPC) - buffer)
|
||||
#define PTR_TO(F) (buffer + parser->F)
|
||||
|
||||
/** Machine **/
|
||||
|
||||
|
||||
#line 59 "http11_response.rl"
|
||||
|
||||
|
||||
/** Data **/
|
||||
|
||||
#line 25 "http11_response.c"
|
||||
static const int http_parser_start = 1;
|
||||
static const int http_parser_first_final = 20;
|
||||
static const int http_parser_error = 0;
|
||||
|
||||
static const int http_parser_en_main = 1;
|
||||
|
||||
|
||||
#line 63 "http11_response.rl"
|
||||
|
||||
int http_parser_init(http_parser *parser) {
|
||||
int cs = 0;
|
||||
|
||||
#line 38 "http11_response.c"
|
||||
{
|
||||
cs = http_parser_start;
|
||||
}
|
||||
|
||||
#line 67 "http11_response.rl"
|
||||
parser->cs = cs;
|
||||
parser->body_start = 0;
|
||||
parser->content_len = 0;
|
||||
parser->mark = 0;
|
||||
parser->nread = 0;
|
||||
parser->field_len = 0;
|
||||
parser->field_start = 0;
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/** exec **/
|
||||
size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off) {
|
||||
const char *p, *pe;
|
||||
int cs = parser->cs;
|
||||
|
||||
assert(off <= len && "offset past end of buffer");
|
||||
|
||||
p = buffer+off;
|
||||
pe = buffer+len;
|
||||
|
||||
assert(pe - p == len - off && "pointers aren't same distance");
|
||||
|
||||
|
||||
#line 69 "http11_response.c"
|
||||
{
|
||||
if ( p == pe )
|
||||
goto _test_eof;
|
||||
switch ( cs )
|
||||
{
|
||||
case 1:
|
||||
if ( (*p) == 72 )
|
||||
goto tr0;
|
||||
goto st0;
|
||||
st0:
|
||||
cs = 0;
|
||||
goto _out;
|
||||
tr0:
|
||||
#line 20 "http11_response.rl"
|
||||
{MARK(mark, p); }
|
||||
goto st2;
|
||||
st2:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof2;
|
||||
case 2:
|
||||
#line 90 "http11_response.c"
|
||||
if ( (*p) == 84 )
|
||||
goto st3;
|
||||
goto st0;
|
||||
st3:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof3;
|
||||
case 3:
|
||||
if ( (*p) == 84 )
|
||||
goto st4;
|
||||
goto st0;
|
||||
st4:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof4;
|
||||
case 4:
|
||||
if ( (*p) == 80 )
|
||||
goto st5;
|
||||
goto st0;
|
||||
st5:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof5;
|
||||
case 5:
|
||||
if ( (*p) == 47 )
|
||||
goto st6;
|
||||
goto st0;
|
||||
st6:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof6;
|
||||
case 6:
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto st7;
|
||||
goto st0;
|
||||
st7:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof7;
|
||||
case 7:
|
||||
if ( (*p) == 46 )
|
||||
goto st8;
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto st7;
|
||||
goto st0;
|
||||
st8:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof8;
|
||||
case 8:
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto st9;
|
||||
goto st0;
|
||||
st9:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof9;
|
||||
case 9:
|
||||
if ( (*p) == 32 )
|
||||
goto tr9;
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto st9;
|
||||
goto st0;
|
||||
tr9:
|
||||
#line 35 "http11_response.rl"
|
||||
{
|
||||
if(parser->http_version != NULL)
|
||||
parser->http_version(parser->data, PTR_TO(mark), LEN(mark, p));
|
||||
}
|
||||
goto st10;
|
||||
st10:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof10;
|
||||
case 10:
|
||||
#line 158 "http11_response.c"
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto tr10;
|
||||
goto st0;
|
||||
tr10:
|
||||
#line 20 "http11_response.rl"
|
||||
{MARK(mark, p); }
|
||||
goto st11;
|
||||
st11:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof11;
|
||||
case 11:
|
||||
#line 170 "http11_response.c"
|
||||
if ( (*p) == 32 )
|
||||
goto tr11;
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto st11;
|
||||
goto st0;
|
||||
tr11:
|
||||
#line 40 "http11_response.rl"
|
||||
{
|
||||
if(parser->status_code != NULL)
|
||||
parser->status_code(parser->data, PTR_TO(mark), LEN(mark,p));
|
||||
}
|
||||
goto st12;
|
||||
st12:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof12;
|
||||
case 12:
|
||||
#line 187 "http11_response.c"
|
||||
if ( (*p) < 11 ) {
|
||||
if ( 0 <= (*p) && (*p) <= 9 )
|
||||
goto tr13;
|
||||
} else if ( (*p) > 12 ) {
|
||||
if ( 14 <= (*p) )
|
||||
goto tr13;
|
||||
} else
|
||||
goto tr13;
|
||||
goto st0;
|
||||
tr13:
|
||||
#line 20 "http11_response.rl"
|
||||
{MARK(mark, p); }
|
||||
goto st13;
|
||||
st13:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof13;
|
||||
case 13:
|
||||
#line 205 "http11_response.c"
|
||||
if ( (*p) == 13 )
|
||||
goto tr15;
|
||||
if ( (*p) > 9 ) {
|
||||
if ( 11 <= (*p) )
|
||||
goto st13;
|
||||
} else if ( (*p) >= 0 )
|
||||
goto st13;
|
||||
goto st0;
|
||||
tr15:
|
||||
#line 45 "http11_response.rl"
|
||||
{
|
||||
if(parser->reason_phrase != NULL)
|
||||
parser->reason_phrase(parser->data, PTR_TO(mark), LEN(mark,p));
|
||||
}
|
||||
goto st14;
|
||||
tr23:
|
||||
#line 27 "http11_response.rl"
|
||||
{ MARK(mark, p); }
|
||||
#line 29 "http11_response.rl"
|
||||
{
|
||||
if(parser->http_field != NULL) {
|
||||
parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
|
||||
}
|
||||
}
|
||||
goto st14;
|
||||
tr26:
|
||||
#line 29 "http11_response.rl"
|
||||
{
|
||||
if(parser->http_field != NULL) {
|
||||
parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
|
||||
}
|
||||
}
|
||||
goto st14;
|
||||
st14:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof14;
|
||||
case 14:
|
||||
#line 243 "http11_response.c"
|
||||
if ( (*p) == 10 )
|
||||
goto st15;
|
||||
goto st0;
|
||||
st15:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof15;
|
||||
case 15:
|
||||
switch( (*p) ) {
|
||||
case 13: goto st16;
|
||||
case 33: goto tr18;
|
||||
case 124: goto tr18;
|
||||
case 126: goto tr18;
|
||||
}
|
||||
if ( (*p) < 45 ) {
|
||||
if ( (*p) > 39 ) {
|
||||
if ( 42 <= (*p) && (*p) <= 43 )
|
||||
goto tr18;
|
||||
} else if ( (*p) >= 35 )
|
||||
goto tr18;
|
||||
} else if ( (*p) > 46 ) {
|
||||
if ( (*p) < 65 ) {
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto tr18;
|
||||
} else if ( (*p) > 90 ) {
|
||||
if ( 94 <= (*p) && (*p) <= 122 )
|
||||
goto tr18;
|
||||
} else
|
||||
goto tr18;
|
||||
} else
|
||||
goto tr18;
|
||||
goto st0;
|
||||
st16:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof16;
|
||||
case 16:
|
||||
if ( (*p) == 10 )
|
||||
goto tr19;
|
||||
goto st0;
|
||||
tr19:
|
||||
#line 50 "http11_response.rl"
|
||||
{
|
||||
parser->body_start = p - buffer + 1;
|
||||
if(parser->header_done != NULL)
|
||||
parser->header_done(parser->data, p + 1, pe - p - 1);
|
||||
{p++; cs = 20; goto _out;}
|
||||
}
|
||||
goto st20;
|
||||
st20:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof20;
|
||||
case 20:
|
||||
#line 295 "http11_response.c"
|
||||
goto st0;
|
||||
tr18:
|
||||
#line 22 "http11_response.rl"
|
||||
{ MARK(field_start, p); }
|
||||
goto st17;
|
||||
st17:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof17;
|
||||
case 17:
|
||||
#line 305 "http11_response.c"
|
||||
switch( (*p) ) {
|
||||
case 33: goto st17;
|
||||
case 58: goto tr21;
|
||||
case 124: goto st17;
|
||||
case 126: goto st17;
|
||||
}
|
||||
if ( (*p) < 45 ) {
|
||||
if ( (*p) > 39 ) {
|
||||
if ( 42 <= (*p) && (*p) <= 43 )
|
||||
goto st17;
|
||||
} else if ( (*p) >= 35 )
|
||||
goto st17;
|
||||
} else if ( (*p) > 46 ) {
|
||||
if ( (*p) < 65 ) {
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto st17;
|
||||
} else if ( (*p) > 90 ) {
|
||||
if ( 94 <= (*p) && (*p) <= 122 )
|
||||
goto st17;
|
||||
} else
|
||||
goto st17;
|
||||
} else
|
||||
goto st17;
|
||||
goto st0;
|
||||
tr21:
|
||||
#line 23 "http11_response.rl"
|
||||
{
|
||||
parser->field_len = LEN(field_start, p);
|
||||
}
|
||||
goto st18;
|
||||
tr24:
|
||||
#line 27 "http11_response.rl"
|
||||
{ MARK(mark, p); }
|
||||
goto st18;
|
||||
st18:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof18;
|
||||
case 18:
|
||||
#line 344 "http11_response.c"
|
||||
switch( (*p) ) {
|
||||
case 13: goto tr23;
|
||||
case 32: goto tr24;
|
||||
}
|
||||
goto tr22;
|
||||
tr22:
|
||||
#line 27 "http11_response.rl"
|
||||
{ MARK(mark, p); }
|
||||
goto st19;
|
||||
st19:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof19;
|
||||
case 19:
|
||||
#line 358 "http11_response.c"
|
||||
if ( (*p) == 13 )
|
||||
goto tr26;
|
||||
goto st19;
|
||||
}
|
||||
_test_eof2: cs = 2; goto _test_eof;
|
||||
_test_eof3: cs = 3; goto _test_eof;
|
||||
_test_eof4: cs = 4; goto _test_eof;
|
||||
_test_eof5: cs = 5; goto _test_eof;
|
||||
_test_eof6: cs = 6; goto _test_eof;
|
||||
_test_eof7: cs = 7; goto _test_eof;
|
||||
_test_eof8: cs = 8; goto _test_eof;
|
||||
_test_eof9: cs = 9; goto _test_eof;
|
||||
_test_eof10: cs = 10; goto _test_eof;
|
||||
_test_eof11: cs = 11; goto _test_eof;
|
||||
_test_eof12: cs = 12; goto _test_eof;
|
||||
_test_eof13: cs = 13; goto _test_eof;
|
||||
_test_eof14: cs = 14; goto _test_eof;
|
||||
_test_eof15: cs = 15; goto _test_eof;
|
||||
_test_eof16: cs = 16; goto _test_eof;
|
||||
_test_eof20: cs = 20; goto _test_eof;
|
||||
_test_eof17: cs = 17; goto _test_eof;
|
||||
_test_eof18: cs = 18; goto _test_eof;
|
||||
_test_eof19: cs = 19; goto _test_eof;
|
||||
|
||||
_test_eof: {}
|
||||
_out: {}
|
||||
}
|
||||
|
||||
#line 92 "http11_response.rl"
|
||||
|
||||
if (!http_parser_has_error(parser))
|
||||
parser->cs = cs;
|
||||
parser->nread += p - (buffer + off);
|
||||
|
||||
assert(p <= pe && "buffer overflow after parsing execute");
|
||||
assert(parser->nread <= len && "nread longer than length");
|
||||
assert(parser->body_start <= len && "body starts after buffer end");
|
||||
assert(parser->mark < len && "mark is after buffer end");
|
||||
assert(parser->field_len <= len && "field has length longer than whole buffer");
|
||||
assert(parser->field_start < len && "field starts after buffer end");
|
||||
|
||||
return(parser->nread);
|
||||
}
|
||||
|
||||
int http_parser_finish(http_parser *parser)
|
||||
{
|
||||
if (http_parser_has_error(parser) ) {
|
||||
return -1;
|
||||
} else if (http_parser_is_finished(parser) ) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int http_parser_has_error(http_parser *parser) {
|
||||
return parser->cs == http_parser_error;
|
||||
}
|
||||
|
||||
int http_parser_is_finished(http_parser *parser) {
|
||||
return parser->cs >= http_parser_first_final;
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
|
||||
#ifndef http11_parser_h
|
||||
#define http11_parser_h
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
|
||||
typedef void (*element_cb)(void *data, const char *at, size_t length);
|
||||
typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen);
|
||||
|
||||
typedef struct http_parser {
|
||||
int cs;
|
||||
size_t body_start;
|
||||
int content_len;
|
||||
size_t nread;
|
||||
size_t mark;
|
||||
size_t field_start;
|
||||
size_t field_len;
|
||||
size_t query_start;
|
||||
|
||||
void *data;
|
||||
|
||||
field_cb http_field;
|
||||
|
||||
element_cb http_version;
|
||||
element_cb status_code;
|
||||
element_cb reason_phrase;
|
||||
element_cb header_done;
|
||||
|
||||
} http_parser;
|
||||
|
||||
int http_parser_init(http_parser *parser);
|
||||
int http_parser_finish(http_parser *parser);
|
||||
size_t http_parser_execute(http_parser *parser, const char *data, size_t len, size_t off);
|
||||
int http_parser_has_error(http_parser *parser);
|
||||
int http_parser_is_finished(http_parser *parser);
|
||||
|
||||
#define http_parser_nread(parser) (parser)->nread
|
||||
|
||||
#endif
|
@ -0,0 +1,124 @@
|
||||
|
||||
#include "http11_response.h"
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define LEN(AT, FPC) (FPC - buffer - parser->AT)
|
||||
#define MARK(M,FPC) (parser->M = (FPC) - buffer)
|
||||
#define PTR_TO(F) (buffer + parser->F)
|
||||
|
||||
/** Machine **/
|
||||
|
||||
%%{
|
||||
|
||||
machine http_parser;
|
||||
|
||||
action mark {MARK(mark, fpc); }
|
||||
|
||||
action start_field { MARK(field_start, fpc); }
|
||||
action write_field {
|
||||
parser->field_len = LEN(field_start, fpc);
|
||||
}
|
||||
|
||||
action start_value { MARK(mark, fpc); }
|
||||
|
||||
action write_value {
|
||||
if(parser->http_field != NULL) {
|
||||
parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
|
||||
}
|
||||
}
|
||||
|
||||
action http_version {
|
||||
if(parser->http_version != NULL)
|
||||
parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
||||
}
|
||||
|
||||
action status_code {
|
||||
if(parser->status_code != NULL)
|
||||
parser->status_code(parser->data, PTR_TO(mark), LEN(mark,fpc));
|
||||
}
|
||||
|
||||
action reason_phrase {
|
||||
if(parser->reason_phrase != NULL)
|
||||
parser->reason_phrase(parser->data, PTR_TO(mark), LEN(mark,fpc));
|
||||
}
|
||||
|
||||
action done {
|
||||
parser->body_start = fpc - buffer + 1;
|
||||
if(parser->header_done != NULL)
|
||||
parser->header_done(parser->data, fpc + 1, pe - fpc - 1);
|
||||
fbreak;
|
||||
}
|
||||
|
||||
include http_response_common "http11_response_common.rl";
|
||||
|
||||
}%%
|
||||
|
||||
/** Data **/
|
||||
%% write data;
|
||||
|
||||
int http_parser_init(http_parser *parser) {
|
||||
int cs = 0;
|
||||
%% write init;
|
||||
parser->cs = cs;
|
||||
parser->body_start = 0;
|
||||
parser->content_len = 0;
|
||||
parser->mark = 0;
|
||||
parser->nread = 0;
|
||||
parser->field_len = 0;
|
||||
parser->field_start = 0;
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/** exec **/
|
||||
size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off) {
|
||||
const char *p, *pe;
|
||||
int cs = parser->cs;
|
||||
|
||||
assert(off <= len && "offset past end of buffer");
|
||||
|
||||
p = buffer+off;
|
||||
pe = buffer+len;
|
||||
|
||||
assert(pe - p == len - off && "pointers aren't same distance");
|
||||
|
||||
%% write exec;
|
||||
|
||||
if (!http_parser_has_error(parser))
|
||||
parser->cs = cs;
|
||||
parser->nread += p - (buffer + off);
|
||||
|
||||
assert(p <= pe && "buffer overflow after parsing execute");
|
||||
assert(parser->nread <= len && "nread longer than length");
|
||||
assert(parser->body_start <= len && "body starts after buffer end");
|
||||
assert(parser->mark < len && "mark is after buffer end");
|
||||
assert(parser->field_len <= len && "field has length longer than whole buffer");
|
||||
assert(parser->field_start < len && "field starts after buffer end");
|
||||
|
||||
return(parser->nread);
|
||||
}
|
||||
|
||||
int http_parser_finish(http_parser *parser)
|
||||
{
|
||||
if (http_parser_has_error(parser) ) {
|
||||
return -1;
|
||||
} else if (http_parser_is_finished(parser) ) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int http_parser_has_error(http_parser *parser) {
|
||||
return parser->cs == http_parser_error;
|
||||
}
|
||||
|
||||
int http_parser_is_finished(http_parser *parser) {
|
||||
return parser->cs >= http_parser_first_final;
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
%%{
|
||||
|
||||
machine http_response_common;
|
||||
|
||||
#### HTTP PROTOCOL GRAMMAR
|
||||
# line endings
|
||||
CRLF = "\r\n";
|
||||
|
||||
# character types
|
||||
CTL = (cntrl | 127);
|
||||
tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
|
||||
|
||||
# elements
|
||||
token = (ascii -- (CTL | tspecials));
|
||||
|
||||
Reason_Phrase = ( ascii -- ("\r" | "\n") )+ >mark %reason_phrase;
|
||||
|
||||
Status_Code = ( digit+ ) >mark %status_code ;
|
||||
|
||||
http_number = ( digit+ "." digit+ ) ;
|
||||
HTTP_Version = ( "HTTP/" http_number ) >mark %http_version ;
|
||||
|
||||
Response_Line = ( HTTP_Version " " Status_Code " " Reason_Phrase CRLF ) ;
|
||||
|
||||
field_name = ( token -- ":" )+ >start_field %write_field;
|
||||
|
||||
field_value = any* >start_value %write_value;
|
||||
|
||||
message_header = field_name ":" " "* field_value :> CRLF;
|
||||
|
||||
Response = Response_Line ( message_header )* ( CRLF @done );
|
||||
|
||||
main := Response;
|
||||
|
||||
}%%
|
@ -0,0 +1,115 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "http11_response.h"
|
||||
#include <ctype.h>
|
||||
|
||||
#define BUFF_LEN 4096
|
||||
|
||||
void http_field(void *data, const char *field,
|
||||
size_t flen, const char *value, size_t vlen)
|
||||
{
|
||||
char buff[BUFF_LEN] = {0};
|
||||
|
||||
strncpy(buff, field, flen);
|
||||
strcat(buff, ": ");
|
||||
strncat(buff, value, vlen);
|
||||
|
||||
printf("HEADER: \"%s\"\n", buff);
|
||||
}
|
||||
|
||||
void http_version(void *data, const char *at, size_t length)
|
||||
{
|
||||
printf("VERSION: \"%.*s\"\n", length, at);
|
||||
}
|
||||
|
||||
void status_code(void *data, const char *at, size_t length)
|
||||
{
|
||||
printf("STATUS_CODE: \"%.*s\"\n", length, at);
|
||||
}
|
||||
|
||||
void reason_phrase(void *data, const char *at, size_t length)
|
||||
{
|
||||
printf("REASON_PHRASE: \"%.*s\"\n", length, at);
|
||||
}
|
||||
|
||||
void header_done(void *data, const char *at, size_t length)
|
||||
{
|
||||
printf("HEADER_DONE.\n");
|
||||
}
|
||||
|
||||
void parser_init(http_parser *hp)
|
||||
{
|
||||
hp->http_field = http_field;
|
||||
hp->http_version = http_version;
|
||||
hp->status_code = status_code;
|
||||
hp->reason_phrase = reason_phrase;
|
||||
hp->header_done = header_done;
|
||||
http_parser_init(hp);
|
||||
}
|
||||
|
||||
int main1 ()
|
||||
{
|
||||
char *data = "HTTP/1.0 200 OK\r\n"
|
||||
"Server: nginx\r\n"
|
||||
"Date: Fri, 26 Mar 2010 03:39:03 GMT\r\n"
|
||||
"Content-Type: text/html; charset=GBK\r\n"
|
||||
"Vary: Accept-Encoding\r\n"
|
||||
"Expires: Fri, 26 Mar 2010 03:40:23 GMT\r\n"
|
||||
"Cache-Control: max-age=80\r\n"
|
||||
"Vary: User-Agent\r\n"
|
||||
"Vary: Accept\r\n"
|
||||
"X-Cache: MISS from cache.163.com\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n"
|
||||
"I am the body"
|
||||
;
|
||||
size_t dlen;
|
||||
http_parser parser, *hp;
|
||||
|
||||
hp = &parser;
|
||||
dlen = strlen(data);
|
||||
|
||||
parser_init(hp);
|
||||
|
||||
http_parser_execute(hp, data, dlen, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
char *data = "HTTP/1.0 200 OK\r\n"
|
||||
"Server: nginx\r\n"
|
||||
"Date: Fri, 26 Mar 2010 03:39:03 GMT\r\n"
|
||||
"Content-Type: text/html; charset=GBK\r\n"
|
||||
"Vary: Accept-Encoding\r\n"
|
||||
"Expires: Fri, 26 Mar 2010 03:40:23 GMT\r\n"
|
||||
"Cache-Control: max-age=80\r\n"
|
||||
"Vary: User-Agent\r\n"
|
||||
"Vary: Accept\r\n"
|
||||
"X-Cache: MISS from cache.163.com\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n"
|
||||
"I am the body"
|
||||
;
|
||||
|
||||
|
||||
size_t dlen, dlen1;
|
||||
http_parser parser, *hp;
|
||||
int i;
|
||||
|
||||
hp = &parser;
|
||||
dlen = strlen(data);
|
||||
|
||||
for (i = 1;i < dlen;i++) {
|
||||
printf("\n\nblock point: %d\n", i);
|
||||
parser_init(hp);
|
||||
dlen1 = http_parser_execute(hp, data, i, 0);
|
||||
dlen1 = http_parser_execute(hp, data, dlen, dlen1);
|
||||
printf("BODY: \"%s\"\n", data + hp->body_start);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "http11_parser.h"
|
||||
#include <ctype.h>
|
||||
|
||||
#define BUFF_LEN 4096
|
||||
|
||||
void http_field(void *data, const char *field,
|
||||
size_t flen, const char *value, size_t vlen)
|
||||
{
|
||||
char buff[BUFF_LEN] = {0};
|
||||
|
||||
strncpy(buff, field, flen);
|
||||
strcat(buff, ": ");
|
||||
strncat(buff, value, vlen);
|
||||
|
||||
printf("HEADER: \"%s\"\n", buff);
|
||||
}
|
||||
|
||||
void request_method(void *data, const char *at, size_t length)
|
||||
{
|
||||
char buff[BUFF_LEN] = {0};
|
||||
|
||||
strncpy(buff, at, length);
|
||||
|
||||
printf("METHOD: \"%s\"\n", buff);
|
||||
}
|
||||
|
||||
void request_uri(void *data, const char *at, size_t length)
|
||||
{
|
||||
char buff[BUFF_LEN] = {0};
|
||||
|
||||
strncpy(buff, at, length);
|
||||
|
||||
printf("URI: \"%s\"\n", buff);
|
||||
}
|
||||
|
||||
void fragment(void *data, const char *at, size_t length)
|
||||
{
|
||||
char buff[BUFF_LEN] = {0};
|
||||
|
||||
strncpy(buff, at, length);
|
||||
|
||||
printf("FRAGMENT: \"%s\"\n", buff);
|
||||
}
|
||||
|
||||
void request_path(void *data, const char *at, size_t length)
|
||||
{
|
||||
char buff[BUFF_LEN] = {0};
|
||||
|
||||
strncpy(buff, at, length);
|
||||
|
||||
printf("PATH: \"%s\"\n", buff);
|
||||
}
|
||||
|
||||
void query_string(void *data, const char *at, size_t length)
|
||||
{
|
||||
char buff[BUFF_LEN] = {0};
|
||||
|
||||
strncpy(buff, at, length);
|
||||
|
||||
printf("QUERY: \"%s\"\n", buff);
|
||||
}
|
||||
|
||||
void http_version(void *data, const char *at, size_t length)
|
||||
{
|
||||
char buff[BUFF_LEN] = {0};
|
||||
|
||||
strncpy(buff, at, length);
|
||||
|
||||
printf("VERSION: \"%s\"\n", buff);
|
||||
}
|
||||
|
||||
void header_done(void *data, const char *at, size_t length)
|
||||
{
|
||||
printf("done.\n");
|
||||
}
|
||||
|
||||
void parser_init(http_parser *hp)
|
||||
{
|
||||
hp->http_field = http_field;
|
||||
hp->request_method = request_method;
|
||||
hp->request_uri = request_uri;
|
||||
hp->fragment = fragment;
|
||||
hp->request_path = request_path;
|
||||
hp->query_string = query_string;
|
||||
hp->http_version = http_version;
|
||||
hp->header_done = header_done;
|
||||
http_parser_init(hp);
|
||||
}
|
||||
|
||||
int main1 ()
|
||||
{
|
||||
char *data = "GET / HTTP/1.0\r\n"
|
||||
"User-Agent: Wget/1.11.4\r\n"
|
||||
"Accept: */*\r\n"
|
||||
"Host: www.163.com\r\n"
|
||||
"Connection: Keep-Alive\r\n"
|
||||
"\r\n";
|
||||
size_t dlen;
|
||||
http_parser parser, *hp;
|
||||
|
||||
hp = &parser;
|
||||
dlen = strlen(data);
|
||||
|
||||
parser_init(hp);
|
||||
|
||||
http_parser_execute(hp, data, dlen, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
char *data = "GET / HTTP/1.0\r\n"
|
||||
"User-Agent: Wget/1.11.4\r\n"
|
||||
"Accept: */*\r\n"
|
||||
"Host: www.163.com\r\n"
|
||||
"Connection: Keep-Alive\r\n"
|
||||
"\r\n";
|
||||
|
||||
|
||||
size_t dlen, dlen1;
|
||||
http_parser parser, *hp;
|
||||
int i;
|
||||
|
||||
hp = &parser;
|
||||
dlen = strlen(data);
|
||||
|
||||
for (i = 1;i < dlen;i++) {
|
||||
parser_init(hp);
|
||||
dlen1 = http_parser_execute(hp, data, i, 0);
|
||||
dlen1 = http_parser_execute(hp, data, dlen, dlen1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,522 @@
|
||||
# vi:filetype=perl
|
||||
|
||||
use lib 'lib';
|
||||
use Test::Nginx::LWP;
|
||||
|
||||
plan tests => repeat_each(2) * 3 * blocks();
|
||||
|
||||
no_root_location();
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: the http_check interface, default type
|
||||
--- http_config
|
||||
upstream backend {
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
server 127.0.0.1:1973;
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1974;
|
||||
server 127.0.0.1:1975;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
location /status {
|
||||
check_status;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /status
|
||||
--- response_headers
|
||||
Content-Type: text/html
|
||||
--- response_body_like: ^.*Check upstream server number: 6.*$
|
||||
|
||||
=== TEST 2: the http_check interface, html
|
||||
--- http_config
|
||||
upstream backend {
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
server 127.0.0.1:1973;
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1974;
|
||||
server 127.0.0.1:1975;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
location /status {
|
||||
check_status html;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /status
|
||||
--- response_headers
|
||||
Content-Type: text/html
|
||||
--- response_body_like: ^.*Check upstream server number: 6.*$
|
||||
|
||||
=== TEST 3: the http_check interface, csv
|
||||
--- http_config
|
||||
upstream backend {
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
server 127.0.0.1:1973;
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1974;
|
||||
server 127.0.0.1:1975;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
location /status {
|
||||
check_status csv;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /status
|
||||
--- response_headers
|
||||
Content-Type: text/plain
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 4: the http_check interface, json
|
||||
--- http_config
|
||||
upstream backend {
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
server 127.0.0.1:1973;
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1974;
|
||||
server 127.0.0.1:1975;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
location /status {
|
||||
check_status json;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /status
|
||||
--- response_headers
|
||||
Content-Type: application/json
|
||||
--- response_body_like: ^.*"total": 6,.*$
|
||||
|
||||
=== TEST 5: the http_check interface, default html, request csv
|
||||
--- http_config
|
||||
upstream backend {
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
server 127.0.0.1:1973;
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1974;
|
||||
server 127.0.0.1:1975;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
location /status {
|
||||
check_status html;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /status?format=csv
|
||||
--- response_headers
|
||||
Content-Type: text/plain
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 6: the http_check interface, default csv, request json
|
||||
--- http_config
|
||||
upstream backend {
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
server 127.0.0.1:1973;
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1974;
|
||||
server 127.0.0.1:1975;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
location /status {
|
||||
check_status csv;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /status?format=json
|
||||
--- response_headers
|
||||
Content-Type: application/json
|
||||
--- response_body_like: ^.*"total": 6,.*$
|
||||
|
||||
=== TEST 7: the http_check interface, default json, request html
|
||||
--- http_config
|
||||
upstream backend {
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
server 127.0.0.1:1973;
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1974;
|
||||
server 127.0.0.1:1975;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
location /status {
|
||||
check_status json;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /status?format=html
|
||||
--- response_headers
|
||||
Content-Type: text/html
|
||||
--- response_body_like: ^.*Check upstream server number: 6.*$
|
||||
|
||||
=== TEST 8: the http_check interface, default json, request htm, bad format
|
||||
--- http_config
|
||||
upstream backend {
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
server 127.0.0.1:1973;
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1974;
|
||||
server 127.0.0.1:1975;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
location /status {
|
||||
check_status json;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /status?format=htm
|
||||
--- response_headers
|
||||
Content-Type: application/json
|
||||
--- response_body_like: ^.*"total": 6,.*$
|
||||
|
||||
=== TEST 9: the http_check interface, default html, request csv and up
|
||||
--- http_config
|
||||
upstream backend {
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
server 127.0.0.1:1973;
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1974;
|
||||
server 127.0.0.1:1975;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
location /status {
|
||||
check_status html;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /status?format=csv&status=up
|
||||
--- response_headers
|
||||
Content-Type: text/plain
|
||||
--- response_body_like: ^[:\.,\w]+\n$
|
||||
|
||||
=== TEST 10: the http_check interface, default csv, request json and down
|
||||
--- http_config
|
||||
upstream backend {
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
server 127.0.0.1:1973;
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1974;
|
||||
server 127.0.0.1:1975;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
location /status {
|
||||
check_status csv;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /status?format=json&status=down
|
||||
--- response_headers
|
||||
Content-Type: application/json
|
||||
--- response_body_like: ^.*"total": 5,.*$
|
||||
|
||||
=== TEST 11: the http_check interface, default json, request html and up
|
||||
--- http_config
|
||||
upstream backend {
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
server 127.0.0.1:1973;
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1974;
|
||||
server 127.0.0.1:1975;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=2000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
location /status {
|
||||
check_status json;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /status?format=html&status=up
|
||||
--- response_headers
|
||||
Content-Type: text/html
|
||||
--- response_body_like: ^.*Check upstream server number: 1.*$
|
||||
|
||||
=== TEST 12: the http_check interface, default json, request html, bad status
|
||||
--- http_config
|
||||
upstream backend {
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
server 127.0.0.1:1973;
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1974;
|
||||
server 127.0.0.1:1975;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
location /status {
|
||||
check_status json;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /status?format=html&status=foo
|
||||
--- response_headers
|
||||
Content-Type: text/html
|
||||
--- response_body_like: ^.*Check upstream server number: 6.*$
|
||||
|
||||
=== TEST 13: the http_check interface, with check_keepalive_requests configured
|
||||
--- http_config
|
||||
upstream backend {
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
server 127.0.0.1:1973;
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1974;
|
||||
server 127.0.0.1:1975;
|
||||
|
||||
check_keepalive_requests 10;
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
location /status {
|
||||
check_status;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /status
|
||||
--- response_headers
|
||||
Content-Type: text/html
|
||||
--- response_body_like: ^.*Check upstream server number: 6.*$
|
@ -0,0 +1,540 @@
|
||||
# vi:filetype=perl
|
||||
|
||||
use lib 'lib';
|
||||
use Test::Nginx::LWP;
|
||||
|
||||
plan tests => repeat_each(2) * 2 * blocks();
|
||||
|
||||
no_root_location();
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: the http_check test-single server
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^<(.*)>$
|
||||
|
||||
=== TEST 2: the http_check test-multi_server
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1971;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
upstream foo{
|
||||
server www.taobao.com:80;
|
||||
server www.taobao.com:81;
|
||||
|
||||
check interval=3000 rise=1 fall=5 timeout=2000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 3: the http_check test
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1971;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET /foo HTTP/1.0\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- error_code: 502
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 4: the http_check without check directive
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1971;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^<(.*)>$
|
||||
|
||||
=== TEST 5: the http_check which does not use the upstream
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1971;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:1970;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^<(.*)>$
|
||||
|
||||
=== TEST 6: the http_check test-single server
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
ip_hash;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^<(.*)>$
|
||||
|
||||
=== TEST 7: the http_check test-multi_server
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1971;
|
||||
ip_hash;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^<(.*)>$
|
||||
|
||||
=== TEST 8: the http_check test
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1971;
|
||||
ip_hash;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET /foo HTTP/1.0\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- error_code: 502
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 9: the http_check without check directive
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1971;
|
||||
ip_hash;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^<(.*)>$
|
||||
|
||||
=== TEST 10: the http_check which does not use the upstream
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1971;
|
||||
ip_hash;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:1970;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^<(.*)>$
|
||||
|
||||
=== TEST 11: the http_check which does not use the upstream, with variable
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1971;
|
||||
ip_hash;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
resolver 8.8.8.8;
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
set $test "/";
|
||||
proxy_pass http://www.taobao.com$test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 12: the http_check test-single server, least conn
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
least_conn;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^<(.*)>$
|
||||
|
||||
=== TEST 13: the http_check test-multi_server, least conn
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1971;
|
||||
least_conn;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^<(.*)>$
|
||||
|
||||
=== TEST 14: the http_check test, least conn
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1971;
|
||||
least_conn;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET /foo HTTP/1.0\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- error_code: 502
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 15: the http_check without check directive, least conn
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1971;
|
||||
least_conn;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^<(.*)>$
|
||||
|
||||
=== TEST 16: the http_check with port
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
check interval=2000 rise=1 fall=1 timeout=1000 type=http port=1971;
|
||||
check_http_send "GET / HTTP/1.0\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- error_code: 502
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 17: the http_check with port
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1971;
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http port=1970;
|
||||
check_http_send "GET / HTTP/1.0\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- error_code: 502
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 18: the http_check with check_keepalive_requests configured
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
check_keepalive_requests 10;
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=http;
|
||||
check_http_send "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^<(.*)>$
|
@ -0,0 +1,162 @@
|
||||
# vi:filetype=perl
|
||||
|
||||
use lib 'lib';
|
||||
use Test::Nginx::LWP;
|
||||
|
||||
plan tests => repeat_each(2) * 2 * blocks();
|
||||
|
||||
no_root_location();
|
||||
#no_diff;
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: the ssl_hello_check test
|
||||
--- http_config
|
||||
upstream test{
|
||||
server www.alipay.com:443;
|
||||
server www.alipay.com:444;
|
||||
server www.alipay.com:445;
|
||||
|
||||
check interval=4000 rise=1 fall=1 timeout=2000 type=ssl_hello;
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_ssl_session_reuse off;
|
||||
proxy_set_header Host "www.alipay.com";
|
||||
proxy_pass https://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 2: the ssl_hello_check test with ip_hash
|
||||
--- http_config
|
||||
upstream test{
|
||||
server www.alipay.com:443;
|
||||
server www.alipay.com:444;
|
||||
server www.alipay.com:445;
|
||||
ip_hash;
|
||||
|
||||
check interval=4000 rise=1 fall=1 timeout=2000 type=ssl_hello;
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_ssl_session_reuse off;
|
||||
proxy_set_header Host "www.alipay.com";
|
||||
proxy_pass https://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 3: the ssl_hello_check test with bad ip
|
||||
--- http_config
|
||||
upstream test{
|
||||
server www.alipay.com:80;
|
||||
server www.alipay.com:443;
|
||||
server www.alipay.com:444;
|
||||
server www.alipay.com:445;
|
||||
|
||||
check interval=4000 rise=1 fall=1 timeout=2000 type=ssl_hello;
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_ssl_session_reuse off;
|
||||
proxy_set_header Host "www.alipay.com";
|
||||
proxy_pass https://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 4: the ssl_hello_check test with least_conn
|
||||
--- http_config
|
||||
upstream test{
|
||||
server www.alipay.com:443;
|
||||
server www.alipay.com:444;
|
||||
server www.alipay.com:445;
|
||||
least_conn;
|
||||
|
||||
check interval=4000 rise=1 fall=1 timeout=2000 type=ssl_hello;
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_ssl_session_reuse off;
|
||||
proxy_set_header Host "www.alipay.com";
|
||||
proxy_pass https://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 5: the ssl_hello_check test with port 80
|
||||
--- http_config
|
||||
upstream test{
|
||||
server www.nginx.org:443;
|
||||
|
||||
check interval=4000 rise=1 fall=1 timeout=2000 type=http port=80;
|
||||
check_http_send "GET / HTTP/1.0\r\n\r\n";
|
||||
check_http_expect_alive http_2xx http_3xx;
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_ssl_session_reuse off;
|
||||
proxy_set_header Host "www.nginx.org";
|
||||
proxy_pass https://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- error_code: 502
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 6: the ssl_hello_check test with port 443
|
||||
--- http_config
|
||||
upstream test{
|
||||
server www.alipay.com:443;
|
||||
|
||||
check interval=4000 rise=1 fall=1 timeout=2000 type=ssl_hello port=443;
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_ssl_session_reuse off;
|
||||
proxy_set_header Host "www.alipay.com";
|
||||
proxy_pass https://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 7: the ssl_hello_check test with port 444
|
||||
--- http_config
|
||||
upstream test{
|
||||
server www.alipay.com:443;
|
||||
|
||||
check interval=4000 rise=1 fall=1 timeout=2000 type=ssl_hello port=444;
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_ssl_session_reuse off;
|
||||
proxy_set_header Host "www.alipay.com";
|
||||
proxy_pass https://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- error_code: 502
|
||||
--- response_body_like: ^.*$
|
||||
|
@ -0,0 +1,208 @@
|
||||
# vi:filetype=perl
|
||||
|
||||
use lib 'lib';
|
||||
use Test::Nginx::LWP;
|
||||
|
||||
plan tests => repeat_each(2) * 2 * blocks();
|
||||
|
||||
no_root_location();
|
||||
#no_diff;
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: the tcp_check test
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^<(.*)>$
|
||||
|
||||
=== TEST 2: the tcp_check test with ip_hash
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
ip_hash;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=tcp;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^<(.*)>$
|
||||
|
||||
=== TEST 3: the tcp_check test which don't use the checked upstream
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:1970;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^<(.*)>$
|
||||
|
||||
=== TEST 3: the tcp_check test with least_conn;
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
server 127.0.0.1:1971;
|
||||
server 127.0.0.1:1972;
|
||||
least_conn;
|
||||
|
||||
check interval=3000 rise=1 fall=5 timeout=1000 type=tcp;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^<(.*)>$
|
||||
|
||||
=== TEST 4: the tcp_check test with port
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1971;
|
||||
|
||||
check interval=3000 rise=1 fall=1 timeout=1000 type=tcp port=1970;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- error_code: 502
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 5: the tcp_check test with port
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
|
||||
check interval=2000 rise=1 fall=1 timeout=1000 type=tcp port=1971;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- error_code: 502
|
||||
--- response_body_like: ^.*$
|
||||
|
||||
=== TEST 5: the tcp_check test with check_keepalive_requests configured
|
||||
--- http_config
|
||||
upstream test{
|
||||
server 127.0.0.1:1970;
|
||||
|
||||
check_keepalive_requests 10;
|
||||
check interval=2000 rise=1 fall=1 timeout=1000 type=tcp;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1970;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
|
||||
--- config
|
||||
location / {
|
||||
proxy_pass http://test;
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /
|
||||
--- response_body_like: ^<(.*)>$
|
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
TEST_NGINX_SLEEP=1 TEST_NGINX_USE_HUP=1 PATH=/home/yaoweibin/nginx/sbin:$PATH prove -r t
|
@ -0,0 +1,91 @@
|
||||
diff --git a/ngx_http_upstream_fair_module.c b/ngx_http_upstream_fair_module.c
|
||||
index a4419ca..af80bba 100644
|
||||
--- a/ngx_http_upstream_fair_module.c
|
||||
+++ b/ngx_http_upstream_fair_module.c
|
||||
@@ -9,6 +9,10 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+#include "ngx_http_upstream_check_module.h"
|
||||
+#endif
|
||||
+
|
||||
typedef struct {
|
||||
ngx_uint_t nreq;
|
||||
ngx_uint_t total_req;
|
||||
@@ -42,6 +42,10 @@ typedef struct {
|
||||
ngx_uint_t max_fails;
|
||||
time_t fail_timeout;
|
||||
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_uint_t check_index;
|
||||
+#endif
|
||||
+
|
||||
time_t accessed;
|
||||
ngx_uint_t down:1;
|
||||
|
||||
@@ -474,6 +478,15 @@ ngx_http_upstream_init_fair_rr(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
|
||||
peers->peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peers->peer[n].down = server[i].down;
|
||||
peers->peer[n].weight = server[i].down ? 0 : server[i].weight;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ peers->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ peers->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
n++;
|
||||
}
|
||||
}
|
||||
@@ -524,6 +537,15 @@ ngx_http_upstream_init_fair_rr(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
|
||||
backup->peer[n].max_fails = server[i].max_fails;
|
||||
backup->peer[n].fail_timeout = server[i].fail_timeout;
|
||||
backup->peer[n].down = server[i].down;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ if (!server[i].down) {
|
||||
+ backup->peer[n].check_index =
|
||||
+ ngx_http_upstream_check_add_peer(cf, us, &server[i].addrs[j]);
|
||||
+ }
|
||||
+ else {
|
||||
+ backup->peer[n].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+ }
|
||||
+#endif
|
||||
n++;
|
||||
}
|
||||
}
|
||||
@@ -580,6 +602,9 @@ ngx_http_upstream_init_fair_rr(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
|
||||
peers->peer[i].weight = 1;
|
||||
peers->peer[i].max_fails = 1;
|
||||
peers->peer[i].fail_timeout = 10;
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ peers->peer[i].check_index = (ngx_uint_t) NGX_ERROR;
|
||||
+#endif
|
||||
}
|
||||
|
||||
us->peer.data = peers;
|
||||
@@ -723,6 +748,12 @@ ngx_http_upstream_fair_try_peer(ngx_peer_connection_t *pc,
|
||||
peer = &fp->peers->peer[peer_id];
|
||||
|
||||
if (!peer->down) {
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
+ "[upstream_fair] get fair peer, check_index: %ui",
|
||||
+ peer->check_index);
|
||||
+ if (!ngx_http_upstream_check_peer_down(peer->check_index)) {
|
||||
+#endif
|
||||
if (peer->max_fails == 0 || peer->shared->fails < peer->max_fails) {
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -733,6 +764,9 @@ ngx_http_upstream_fair_try_peer(ngx_peer_connection_t *pc,
|
||||
peer->shared->fails = 0;
|
||||
return NGX_OK;
|
||||
}
|
||||
+#if (NGX_HTTP_UPSTREAM_CHECK)
|
||||
+ }
|
||||
+#endif
|
||||
}
|
||||
|
||||
return NGX_BUSY;
|
@ -0,0 +1,5 @@
|
||||
STDIN.each do |line|
|
||||
next unless line
|
||||
res = line.gsub(/\s+$/, "")
|
||||
puts "#{res}"
|
||||
end
|
@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
for file in *
|
||||
do
|
||||
if [ -d "$file" ]
|
||||
then
|
||||
continue
|
||||
fi
|
||||
|
||||
ruby util/chomp.rb < $file > /tmp/tt
|
||||
mv /tmp/tt $file
|
||||
done
|
||||
|
||||
rm -f /tmp/tt
|
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
perl util/wiki2pod.pl doc/README.wiki > /tmp/a.pod && pod2text /tmp/a.pod > doc/README.txt
|
||||
|
||||
cp doc/README.txt README
|
@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use bytes;
|
||||
|
||||
my @nl_counts;
|
||||
my $last_nl_count_level;
|
||||
|
||||
my @bl_counts;
|
||||
my $last_bl_count_level;
|
||||
|
||||
sub fmt_pos ($) {
|
||||
(my $s = $_[0]) =~ s{\#(.*)}{/"$1"};
|
||||
$s;
|
||||
}
|
||||
|
||||
sub fmt_mark ($$) {
|
||||
my ($tag, $s) = @_;
|
||||
my $max_level = 0;
|
||||
while ($s =~ /([<>])\1*/g) {
|
||||
my $level = length $&;
|
||||
if ($level > $max_level) {
|
||||
$max_level = $level;
|
||||
}
|
||||
}
|
||||
|
||||
my $times = $max_level + 1;
|
||||
if ($times > 1) {
|
||||
$s = " $s ";
|
||||
}
|
||||
return $tag . ('<' x $times) . $s . ('>' x $times);
|
||||
}
|
||||
|
||||
print "=encoding utf-8\n\n";
|
||||
|
||||
while (<>) {
|
||||
if ($. == 1) {
|
||||
# strip the leading U+FEFF byte in MS-DOS text files
|
||||
my $first = ord(substr($_, 0, 1));
|
||||
#printf STDERR "0x%x", $first;
|
||||
#my $second = ord(substr($_, 2, 1));
|
||||
#printf STDERR "0x%x", $second;
|
||||
if ($first == 0xEF) {
|
||||
substr($_, 0, 1, '');
|
||||
#warn "Hit!";
|
||||
}
|
||||
}
|
||||
s{\[(http[^ \]]+) ([^\]]*)\]}{$2 (L<$1>)}gi;
|
||||
s{ \[\[ ( [^\]\|]+ ) \| ([^\]]*) \]\] }{"L<$2|" . fmt_pos($1) . ">"}gixe;
|
||||
s{<code>(.*?)</code>}{fmt_mark('C', $1)}gie;
|
||||
s{'''(.*?)'''}{fmt_mark('B', $1)}ge;
|
||||
s{''(.*?)''}{fmt_mark('I', $1)}ge;
|
||||
if (s{^\s*<[^>]+>\s*$}{}) {
|
||||
next;
|
||||
}
|
||||
|
||||
if (/^\s*$/) {
|
||||
print "\n";
|
||||
next;
|
||||
}
|
||||
|
||||
=begin cmt
|
||||
|
||||
if ($. == 1) {
|
||||
warn $_;
|
||||
for my $i (0..length($_) - 1) {
|
||||
my $chr = substr($_, $i, 1);
|
||||
warn "chr ord($i): ".ord($chr)." \"$chr\"\n";
|
||||
}
|
||||
}
|
||||
|
||||
=end cmt
|
||||
=cut
|
||||
|
||||
if (/(=+) (.*) \1$/) {
|
||||
#warn "HERE! $_" if $. == 1;
|
||||
my ($level, $title) = (length $1, $2);
|
||||
collapse_lists();
|
||||
|
||||
print "\n=head$level $title\n\n";
|
||||
} elsif (/^(\#+) (.*)/) {
|
||||
my ($level, $txt) = (length($1) - 1, $2);
|
||||
if (defined $last_nl_count_level && $level != $last_nl_count_level) {
|
||||
print "\n=back\n\n";
|
||||
}
|
||||
$last_nl_count_level = $level;
|
||||
$nl_counts[$level] ||= 0;
|
||||
if ($nl_counts[$level] == 0) {
|
||||
print "\n=over\n\n";
|
||||
}
|
||||
$nl_counts[$level]++;
|
||||
print "\n=item $nl_counts[$level].\n\n";
|
||||
print "$txt\n";
|
||||
} elsif (/^(\*+) (.*)/) {
|
||||
my ($level, $txt) = (length($1) - 1, $2);
|
||||
if (defined $last_bl_count_level && $level != $last_bl_count_level) {
|
||||
print "\n=back\n\n";
|
||||
}
|
||||
$last_bl_count_level = $level;
|
||||
$bl_counts[$level] ||= 0;
|
||||
if ($bl_counts[$level] == 0) {
|
||||
print "\n=over\n\n";
|
||||
}
|
||||
$bl_counts[$level]++;
|
||||
print "\n=item *\n\n";
|
||||
print "$txt\n";
|
||||
} else {
|
||||
collapse_lists();
|
||||
print;
|
||||
}
|
||||
}
|
||||
|
||||
collapse_lists();
|
||||
|
||||
sub collapse_lists {
|
||||
while (defined $last_nl_count_level && $last_nl_count_level >= 0) {
|
||||
print "\n=back\n\n";
|
||||
$last_nl_count_level--;
|
||||
}
|
||||
undef $last_nl_count_level;
|
||||
undef @nl_counts;
|
||||
|
||||
while (defined $last_bl_count_level && $last_bl_count_level >= 0) {
|
||||
print "\n=back\n\n";
|
||||
$last_bl_count_level--;
|
||||
}
|
||||
undef $last_bl_count_level;
|
||||
undef @bl_counts;
|
||||
}
|
||||
|
Loading…
Reference in new issue