summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
authorLorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>2026-01-02 13:52:21 +0100
committerGitHub <noreply@github.com>2026-01-02 13:52:21 +0100
commit1e108210b67b4e1a3bfef60d0a7718b8d0080c1a (patch)
tree3669cfa7ceb8813f82199f6b9b66ad7f7c5d7b65 /plugins
parente2694816e78986f7a97691dc5b013cbeb7eede4f (diff)
parentbfc6492562f6cef4badda192142a0d10a3ed870b (diff)
downloadmonitoring-plugins-1e108210b67b4e1a3bfef60d0a7718b8d0080c1a.tar.gz
Merge branch 'master' into netsnmp595-fix
Diffstat (limited to 'plugins')
-rw-r--r--plugins/Makefile.am2
-rw-r--r--plugins/check_curl.c41
-rw-r--r--plugins/check_curl.d/check_curl_helpers.c30
-rw-r--r--plugins/check_curl.d/check_curl_helpers.h2
-rw-r--r--plugins/check_curl.d/config.h6
-rwxr-xr-xplugins/tests/check_curl.t184
6 files changed, 233 insertions, 32 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index cf06c70c..252734a4 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -164,7 +164,7 @@ check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS)
164check_real_LDADD = $(NETLIBS) 164check_real_LDADD = $(NETLIBS)
165check_snmp_SOURCES = check_snmp.c check_snmp.d/check_snmp_helpers.c 165check_snmp_SOURCES = check_snmp.c check_snmp.d/check_snmp_helpers.c
166check_snmp_LDADD = $(BASEOBJS) 166check_snmp_LDADD = $(BASEOBJS)
167check_snmp_LDFLAGS = $(AM_LDFLAGS) `pkg-config --libs netsnmp` 167check_snmp_LDFLAGS = $(AM_LDFLAGS) -lm `pkg-config --libs netsnmp`
168check_snmp_CFLAGS = $(AM_CFLAGS) `pkg-config --cflags netsnmp` 168check_snmp_CFLAGS = $(AM_CFLAGS) `pkg-config --cflags netsnmp`
169check_smtp_LDADD = $(SSLOBJS) 169check_smtp_LDADD = $(SSLOBJS)
170check_ssh_LDADD = $(NETLIBS) 170check_ssh_LDADD = $(NETLIBS)
diff --git a/plugins/check_curl.c b/plugins/check_curl.c
index 0aff8b40..e7737c7c 100644
--- a/plugins/check_curl.c
+++ b/plugins/check_curl.c
@@ -92,16 +92,16 @@ typedef struct {
92static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); 92static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
93 93
94static mp_subcheck check_http(check_curl_config /*config*/, check_curl_working_state workingState, 94static mp_subcheck check_http(check_curl_config /*config*/, check_curl_working_state workingState,
95 int redir_depth); 95 long redir_depth);
96 96
97typedef struct { 97typedef struct {
98 int redir_depth; 98 long redir_depth;
99 check_curl_working_state working_state; 99 check_curl_working_state working_state;
100 int error_code; 100 int error_code;
101 check_curl_global_state curl_state; 101 check_curl_global_state curl_state;
102} redir_wrapper; 102} redir_wrapper;
103static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, 103static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/,
104 int redir_depth, check_curl_working_state working_state); 104 long redir_depth, check_curl_working_state working_state);
105 105
106static void print_help(void); 106static void print_help(void);
107void print_usage(void); 107void print_usage(void);
@@ -198,7 +198,7 @@ CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) {
198#endif /* HAVE_SSL */ 198#endif /* HAVE_SSL */
199 199
200mp_subcheck check_http(const check_curl_config config, check_curl_working_state workingState, 200mp_subcheck check_http(const check_curl_config config, check_curl_working_state workingState,
201 int redir_depth) { 201 long redir_depth) {
202 202
203 // ======================= 203 // =======================
204 // Initialisation for curl 204 // Initialisation for curl
@@ -441,19 +441,19 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state
441 "CURLINFO_REDIRECT_COUNT"); 441 "CURLINFO_REDIRECT_COUNT");
442 442
443 if (verbose >= 2) { 443 if (verbose >= 2) {
444 printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); 444 printf(_("* curl LIBINFO_REDIRECT_COUNT is %ld\n"), redir_depth);
445 } 445 }
446 446
447 mp_subcheck sc_redir_depth = mp_subcheck_init(); 447 mp_subcheck sc_redir_depth = mp_subcheck_init();
448 if (redir_depth > config.max_depth) { 448 if (redir_depth > config.max_depth) {
449 xasprintf(&sc_redir_depth.output, 449 xasprintf(&sc_redir_depth.output,
450 "maximum redirection depth %d exceeded in libcurl", 450 "maximum redirection depth %ld exceeded in libcurl",
451 config.max_depth); 451 config.max_depth);
452 sc_redir_depth = mp_set_subcheck_state(sc_redir_depth, STATE_CRITICAL); 452 sc_redir_depth = mp_set_subcheck_state(sc_redir_depth, STATE_CRITICAL);
453 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth); 453 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth);
454 return sc_result; 454 return sc_result;
455 } 455 }
456 xasprintf(&sc_redir_depth.output, "redirection depth %d (of a maximum %d)", 456 xasprintf(&sc_redir_depth.output, "redirection depth %ld (of a maximum %ld)",
457 redir_depth, config.max_depth); 457 redir_depth, config.max_depth);
458 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth); 458 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth);
459 459
@@ -653,7 +653,7 @@ char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) {
653} 653}
654 654
655redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config, 655redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config,
656 int redir_depth, check_curl_working_state working_state) { 656 long redir_depth, check_curl_working_state working_state) {
657 curlhelp_statusline status_line; 657 curlhelp_statusline status_line;
658 struct phr_header headers[255]; 658 struct phr_header headers[255];
659 size_t msglen; 659 size_t msglen;
@@ -678,7 +678,7 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config
678 } 678 }
679 679
680 if (++redir_depth > config.max_depth) { 680 if (++redir_depth > config.max_depth) {
681 die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s\n"), 681 die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %ld exceeded - %s\n"),
682 config.max_depth, location); 682 config.max_depth, location);
683 } 683 }
684 684
@@ -761,7 +761,7 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config
761 } 761 }
762 762
763 /* compose new path */ 763 /* compose new path */
764 /* TODO: handle fragments and query part of URL */ 764 /* TODO: handle fragments of URL */
765 char *new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE); 765 char *new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE);
766 if (uri.pathHead) { 766 if (uri.pathHead) {
767 for (UriPathSegmentA *pathSegment = uri.pathHead; pathSegment; 767 for (UriPathSegmentA *pathSegment = uri.pathHead; pathSegment;
@@ -772,6 +772,25 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config
772 } 772 }
773 } 773 }
774 774
775 /* missing components have null,null in their UriTextRangeA
776 * add query parameters if they exist.
777 */
778 if (uri.query.first && uri.query.afterLast){
779 // Ensure we have space for '?' + query_str + '\0' ahead of time, instead of calling strncat twice
780 size_t current_len = strlen(new_url);
781 size_t remaining_space = DEFAULT_BUFFER_SIZE - current_len - 1;
782
783 const char* query_str = uri_string(uri.query, buf, DEFAULT_BUFFER_SIZE);
784 size_t query_str_len = strlen(query_str);
785
786 if (remaining_space >= query_str_len + 1) {
787 strcat(new_url, "?");
788 strcat(new_url, query_str);
789 }else{
790 die(STATE_UNKNOWN, _("HTTP UNKNOWN - No space to add query part of size %d to the buffer, buffer has remaining size %d"), query_str_len , current_len );
791 }
792 }
793
775 if (working_state.serverPort == new_port && 794 if (working_state.serverPort == new_port &&
776 !strncmp(working_state.server_address, new_host, MAX_IPV4_HOSTLENGTH) && 795 !strncmp(working_state.server_address, new_host, MAX_IPV4_HOSTLENGTH) &&
777 (working_state.host_name && 796 (working_state.host_name &&
@@ -1400,7 +1419,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
1400 } 1419 }
1401#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */ 1420#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */
1402 if (verbose >= 2) { 1421 if (verbose >= 2) {
1403 printf(_("* Set SSL/TLS version to %d\n"), result.config.curl_config.ssl_version); 1422 printf(_("* Set SSL/TLS version to %ld\n"), result.config.curl_config.ssl_version);
1404 } 1423 }
1405 if (!specify_port) { 1424 if (!specify_port) {
1406 result.config.initial_config.serverPort = HTTPS_PORT; 1425 result.config.initial_config.serverPort = HTTPS_PORT;
diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c
index e4e7bef6..7be781fc 100644
--- a/plugins/check_curl.d/check_curl_helpers.c
+++ b/plugins/check_curl.d/check_curl_helpers.c
@@ -19,7 +19,7 @@ bool add_sslctx_verify_fun = false;
19check_curl_configure_curl_wrapper 19check_curl_configure_curl_wrapper
20check_curl_configure_curl(const check_curl_static_curl_config config, 20check_curl_configure_curl(const check_curl_static_curl_config config,
21 check_curl_working_state working_state, bool check_cert, 21 check_curl_working_state working_state, bool check_cert,
22 bool on_redirect_dependent, int follow_method, int max_depth) { 22 bool on_redirect_dependent, int follow_method, long max_depth) {
23 check_curl_configure_curl_wrapper result = { 23 check_curl_configure_curl_wrapper result = {
24 .errorcode = OK, 24 .errorcode = OK,
25 .curl_state = 25 .curl_state =
@@ -57,7 +57,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
57 result.curl_state.curl_easy_initialized = true; 57 result.curl_state.curl_easy_initialized = true;
58 58
59 if (verbose >= 1) { 59 if (verbose >= 1) {
60 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1), 60 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1L),
61 "CURLOPT_VERBOSE"); 61 "CURLOPT_VERBOSE");
62 } 62 }
63 63
@@ -214,10 +214,10 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
214 if (working_state.http_method) { 214 if (working_state.http_method) {
215 if (!strcmp(working_state.http_method, "POST")) { 215 if (!strcmp(working_state.http_method, "POST")) {
216 handle_curl_option_return_code( 216 handle_curl_option_return_code(
217 curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1), "CURLOPT_POST"); 217 curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1L), "CURLOPT_POST");
218 } else if (!strcmp(working_state.http_method, "PUT")) { 218 } else if (!strcmp(working_state.http_method, "PUT")) {
219 handle_curl_option_return_code( 219 handle_curl_option_return_code(
220 curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); 220 curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1L), "CURLOPT_UPLOAD");
221 } else { 221 } else {
222 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, 222 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
223 CURLOPT_CUSTOMREQUEST, 223 CURLOPT_CUSTOMREQUEST,
@@ -300,10 +300,10 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
300 /* per default if we have a CA verify both the peer and the 300 /* per default if we have a CA verify both the peer and the
301 * hostname in the certificate, can be switched off later */ 301 * hostname in the certificate, can be switched off later */
302 handle_curl_option_return_code( 302 handle_curl_option_return_code(
303 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1), 303 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1L),
304 "CURLOPT_SSL_VERIFYPEER"); 304 "CURLOPT_SSL_VERIFYPEER");
305 handle_curl_option_return_code( 305 handle_curl_option_return_code(
306 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2), 306 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2L),
307 "CURLOPT_SSL_VERIFYHOST"); 307 "CURLOPT_SSL_VERIFYHOST");
308 } else { 308 } else {
309 /* backward-compatible behaviour, be tolerant in checks 309 /* backward-compatible behaviour, be tolerant in checks
@@ -311,10 +311,10 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
311 * to be less tolerant about ssl verfications 311 * to be less tolerant about ssl verfications
312 */ 312 */
313 handle_curl_option_return_code( 313 handle_curl_option_return_code(
314 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0), 314 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0L),
315 "CURLOPT_SSL_VERIFYPEER"); 315 "CURLOPT_SSL_VERIFYPEER");
316 handle_curl_option_return_code( 316 handle_curl_option_return_code(
317 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0), 317 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0L),
318 "CURLOPT_SSL_VERIFYHOST"); 318 "CURLOPT_SSL_VERIFYHOST");
319 } 319 }
320 320
@@ -438,7 +438,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
438 if (on_redirect_dependent) { 438 if (on_redirect_dependent) {
439 if (follow_method == FOLLOW_LIBCURL) { 439 if (follow_method == FOLLOW_LIBCURL) {
440 handle_curl_option_return_code( 440 handle_curl_option_return_code(
441 curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1), 441 curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1L),
442 "CURLOPT_FOLLOWLOCATION"); 442 "CURLOPT_FOLLOWLOCATION");
443 443
444 /* default -1 is infinite, not good, could lead to zombie plugins! 444 /* default -1 is infinite, not good, could lead to zombie plugins!
@@ -474,7 +474,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
474 } 474 }
475 /* no-body */ 475 /* no-body */
476 if (working_state.no_body) { 476 if (working_state.no_body) {
477 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1), 477 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1L),
478 "CURLOPT_NOBODY"); 478 "CURLOPT_NOBODY");
479 } 479 }
480 480
@@ -796,15 +796,17 @@ mp_subcheck check_document_dates(const curlhelp_write_curlbuf *header_buf, const
796 ((float)last_modified) / (60 * 60 * 24)); 796 ((float)last_modified) / (60 * 60 * 24));
797 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); 797 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
798 } else { 798 } else {
799 xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"), 799 xasprintf(&sc_document_dates.output, _("Last modified %lld:%02d:%02d ago"),
800 last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); 800 (long long)last_modified / (60 * 60), (int)(last_modified / 60) % 60,
801 (int)last_modified % 60);
801 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); 802 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
802 } 803 }
803 } else { 804 } else {
804 // TODO is this the OK case? 805 // TODO is this the OK case?
805 time_t last_modified = (srv_data - doc_data); 806 time_t last_modified = (srv_data - doc_data);
806 xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"), 807 xasprintf(&sc_document_dates.output, _("Last modified %lld:%02d:%02d ago"),
807 last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); 808 (long long)last_modified / (60 * 60), (int)(last_modified / 60) % 60,
809 (int)last_modified % 60);
808 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_OK); 810 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_OK);
809 } 811 }
810 } 812 }
diff --git a/plugins/check_curl.d/check_curl_helpers.h b/plugins/check_curl.d/check_curl_helpers.h
index e7b80f7e..e77b763b 100644
--- a/plugins/check_curl.d/check_curl_helpers.h
+++ b/plugins/check_curl.d/check_curl_helpers.h
@@ -80,7 +80,7 @@ check_curl_configure_curl_wrapper check_curl_configure_curl(check_curl_static_cu
80 check_curl_working_state working_state, 80 check_curl_working_state working_state,
81 bool check_cert, 81 bool check_cert,
82 bool on_redirect_dependent, 82 bool on_redirect_dependent,
83 int follow_method, int max_depth); 83 int follow_method, long max_depth);
84 84
85void handle_curl_option_return_code(CURLcode res, const char *option); 85void handle_curl_option_return_code(CURLcode res, const char *option);
86 86
diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h
index f51b2ee9..61067d46 100644
--- a/plugins/check_curl.d/config.h
+++ b/plugins/check_curl.d/config.h
@@ -57,10 +57,10 @@ typedef struct {
57 bool haproxy_protocol; 57 bool haproxy_protocol;
58 long socket_timeout; 58 long socket_timeout;
59 sa_family_t sin_family; 59 sa_family_t sin_family;
60 int curl_http_version; 60 long curl_http_version;
61 char **http_opt_headers; 61 char **http_opt_headers;
62 size_t http_opt_headers_count; 62 size_t http_opt_headers_count;
63 int ssl_version; 63 long ssl_version;
64 char *client_cert; 64 char *client_cert;
65 char *client_privkey; 65 char *client_privkey;
66 char *ca_cert; 66 char *ca_cert;
@@ -76,7 +76,7 @@ typedef struct {
76 check_curl_working_state initial_config; 76 check_curl_working_state initial_config;
77 77
78 check_curl_static_curl_config curl_config; 78 check_curl_static_curl_config curl_config;
79 int max_depth; 79 long max_depth;
80 int followmethod; 80 int followmethod;
81 int followsticky; 81 int followsticky;
82 82
diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t
index 52c5ad1c..248eb4c5 100755
--- a/plugins/tests/check_curl.t
+++ b/plugins/tests/check_curl.t
@@ -20,9 +20,14 @@ use Test::More;
20use NPTest; 20use NPTest;
21use FindBin qw($Bin); 21use FindBin qw($Bin);
22 22
23use URI;
24use URI::QueryParam;
25use HTTP::Daemon;
26use HTTP::Daemon::SSL;
27
23$ENV{'LC_TIME'} = "C"; 28$ENV{'LC_TIME'} = "C";
24 29
25my $common_tests = 75; 30my $common_tests = 95;
26my $ssl_only_tests = 8; 31my $ssl_only_tests = 8;
27# Check that all dependent modules are available 32# Check that all dependent modules are available
28eval "use HTTP::Daemon 6.01;"; 33eval "use HTTP::Daemon 6.01;";
@@ -186,6 +191,123 @@ sub run_server {
186 $c->send_response('moved to /redirect2'); 191 $c->send_response('moved to /redirect2');
187 } elsif ($r->url->path eq "/redir_timeout") { 192 } elsif ($r->url->path eq "/redir_timeout") {
188 $c->send_redirect( "/timeout" ); 193 $c->send_redirect( "/timeout" );
194 } elsif ($r->url->path =~ m{^/redirect_with_increment}) {
195 # <scheme>://<username>:<password>@<host>:<port>/<path>;<parameters>?<query>#<fragment>
196 # Find every parameter, query , and fragment keys and increment them
197
198 my $content = "";
199
200 # Use URI to help with query/fragment; parse path params manually.
201 my $original_url = $r->url->as_string;
202 $content .= " original_url: ${original_url}\n";
203 my $uri = URI->new($original_url);
204 $content .= " uri: ${uri}\n";
205
206 my $path = $uri->path // '';
207 my $query = $uri->query // '';
208 my $fragment = $uri->fragment // '';
209
210 $content .= " path: ${path}\n";
211 $content .= " query: ${query}\n";
212 $content .= " fragment: ${fragment}\n";
213
214 # split the URI part and parameters. URI package cannot do this
215 # group 1 is captured: anything without a semicolon: ([^;]*)
216 # group 2 is uncaptured: (?:;(.*))?
217 # (?: ... )? prevents capturing the parameter section
218 # inside group 2, ';' matches the first ever semicolon
219 # group3 is captured: any character string : (.*)
220 # \? matches an actual ? mark, which starts the query parameters
221 my ($before_params, $params) = $uri =~ m{^([^;]*)(?:;(.*))?\?};
222 $before_params //= '';
223 $params //= '';
224 $content .= " before_params: ${before_params}\n";
225 $content .= " params: ${params}\n";
226 my @parameter_pairs;
227 if (defined $params && length $params) {
228 for my $p (split /;/, $params) {
229 my ($key,$value) = split /=/, $p, 2;
230 $value //= '';
231 push @parameter_pairs, [ $key, $value ];
232 $content .= " parameter: ${key} -> ${value}\n";
233 }
234 }
235
236 # query parameters are offered directly from the library
237 my @query_form = $uri->query_form;
238 my @query_parameter_pairs;
239 while (@query_form) {
240 my $key = shift @query_form;
241 my $value = shift @query_form;
242 $value //= ''; # there can be valueless keys
243 push @query_parameter_pairs, [ $key, $value ];
244 $content .= " query: ${key} -> ${value}\n";
245 }
246
247 # helper to increment value
248 my $increment = sub {
249 my ($v) = @_;
250 return $v if !defined $v || $v eq '';
251 # numeric integer
252 if ($v =~ /^-?\d+$/) {
253 return $v + 1;
254 }
255 # otherwise -> increment as if its an ascii character
256 # sed replacement syntax, but the $& holds the matched character
257 if (length($v)) {
258 (my $new_v = $v) =~ s/./chr(ord($&) + 1)/ge;
259 return $new_v;
260 }
261 };
262
263 # increment values in pairs
264 for my $pair (@parameter_pairs) {
265 $pair->[1] = $increment->($pair->[1]);
266 $content .= " parameter new: " . $pair->[0] . " -> " . $pair->[1] . "\n";
267 }
268 for my $pair (@query_parameter_pairs) {
269 $pair->[1] = $increment->($pair->[1]);
270 $content .= " query parameter new: " . $pair->[0] . " -> " . $pair->[1] . "\n";
271 }
272
273 # rebuild strings
274 my $new_parameter_str = join(';', map { $_->[0] . '=' . $_->[1] } @parameter_pairs);
275 $content .= " new_parameter_str: ${new_parameter_str}\n";
276
277 # library can rebuild from an array
278 my @new_query_form;
279 for my $p (@query_parameter_pairs) { push @new_query_form, $p->[0], $p->[1] }
280
281 my $new_fragment_str = '';
282 for my $pair (@parameter_pairs) {
283 my $key = $pair->[0];
284 my $value = $pair->[1];
285 if ($key eq "fragment") {
286 $new_fragment_str = $value
287 }
288 }
289 $content .= " new_fragment_str: ${new_fragment_str}\n";
290
291 # construct new URI using the library
292 my $new_uri = URI->new('');
293 $new_uri->path( $before_params . ($new_parameter_str ? ';' . $new_parameter_str : '') );
294 $new_uri->query_form( \@new_query_form ) if @new_query_form;
295 $new_uri->fragment( $new_fragment_str ) if $new_fragment_str ne '';
296 $content .= " new_uri: ${new_uri}\n";
297
298 # Redirect until fail_count or redirect_count reaches 3
299 if ($new_uri =~ /fail_count=3/){
300 $c->send_error(HTTP::Status->RC_FORBIDDEN, "fail count reached 3, url path:" . $r->url->path );
301 } elsif ($new_uri =~ /redirect_count=3/){
302 $c->send_response(HTTP::Response->new( 200, 'OK', undef , $content ));
303 } elsif ($new_uri =~ /location_redirect_count=3/){
304 $c->send_basic_header(302);
305 $c->send_header("Location", "$new_uri" );
306 $c->send_crlf;
307 $c->send_response("$content \n moved to $new_uri");
308 } else {
309 $c->send_redirect( $new_uri->as_string, 301, $content );
310 }
189 } elsif ($r->url->path eq "/timeout") { 311 } elsif ($r->url->path eq "/timeout") {
190 # Keep $c from being destroyed, but prevent severe leaks 312 # Keep $c from being destroyed, but prevent severe leaks
191 unshift @persist, $c; 313 unshift @persist, $c;
@@ -215,7 +337,7 @@ sub run_server {
215 return($chunk); 337 return($chunk);
216 })); 338 }));
217 } else { 339 } else {
218 $c->send_error(HTTP::Status->RC_FORBIDDEN); 340 $c->send_error(HTTP::Status->RC_FORBIDDEN, "unknown url path:" . $r->url->path );
219 } 341 }
220 $c->close; 342 $c->close;
221 } 343 }
@@ -482,6 +604,64 @@ sub run_common_tests {
482 is( $result->return_code, 0, $cmd); 604 is( $result->return_code, 0, $cmd);
483 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); 605 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
484 606
607 # Redirect with increment tests. These are for checking if the url parameters, query parameters and fragment are parsed.
608 # The server at this point has dynamic redirection. It tries to increment values that it sees in these fields, then redirects.
609 # It also appends some debug log and writes it into HTTP content, pass the -vvv parameter to see them.
610
611 $cmd = "$command -u '/redirect_with_increment/path1/path2/path3/path4' --onredirect=follow -vvv";
612 $result = NPTest->testCmd( "$cmd" );
613 is( $result->return_code, 1, $cmd);
614 like( $result->output, '/.*HTTP/1.1 403 Forbidden - \d+ bytes in [\d\.]+ second.*/', "Output correct, redirect_count was not present, got redirected to / : ".$result->output );
615
616 # redirect_count=0 is parsed as a parameter and incremented. When it goes up to 3, the redirection returns HTTP OK
617 $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv";
618 $result = NPTest->testCmd( "$cmd" );
619 is( $result->return_code, 0, $cmd);
620 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, redirect_count went up to 3, and returned OK: ".$result->output );
621
622 # location_redirect_count=0 goes up to 3, which uses the HTTP 302 style of redirection with 'Location' header
623 $cmd = "$command -u '/redirect_with_increment/path1/path2;location_redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv";
624 $result = NPTest->testCmd( "$cmd" );
625 is( $result->return_code, 0, $cmd);
626 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output );
627
628 # fail_count parameter may also go up to 3, which returns a HTTP 403
629 $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;fail_count=2' --onredirect=follow -vvv";
630 $result = NPTest->testCmd( "$cmd" );
631 is( $result->return_code, 1, $cmd);
632 like( $result->output, '/.*HTTP/1.1 403 Forbidden - \d+ bytes in [\d\.]+ second.*/', "Output correct, early due to fail_count reaching 3: ".$result->output );
633
634 # redirect_count=0, p1=1 , p2=ab => redirect_count=1, p1=2 , p2=bc => redirect_count=2, p1=3 , p2=cd => redirect_count=3 , p1=4 , p2=de
635 # Last visited URI returns HTTP OK instead of redirect, and the one before that contains the new_uri in its content
636 $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv";
637 $result = NPTest->testCmd( "$cmd" );
638 is( $result->return_code, 0, $cmd);
639 like( $result->output, '/.*redirect_count=3;p1=4;p2=de\?*/', "Output correct, parsed and incremented both parameters p1 and p2 : ".$result->output );
640 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output );
641
642 # Same incrementation as before, uses the query parameters that come after the first '?' : qp1 and qp2
643 $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv";
644 $result = NPTest->testCmd( "$cmd" );
645 is( $result->return_code, 0, $cmd);
646 like( $result->output, '/.*\?qp1=13&qp2=no*/', "Output correct, parsed and incremented both query parameters qp1 and qp2 : ".$result->output );
647 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output );
648
649 # Check if the query parameter order is kept intact
650 $cmd = "$command -u '/redirect_with_increment;redirect_count=0;?qp0=0&qp1=1&qp2=2&qp3=3&qp4=4&qp5=5' --onredirect=follow -vvv";
651 $result = NPTest->testCmd( "$cmd" );
652 is( $result->return_code, 0, $cmd);
653 like( $result->output, '/.*\?qp0=3&qp1=4&qp2=5&qp3=6&qp4=7&qp5=8*/', "Output correct, parsed and incremented query parameters qp1,qp2,qp3,qp4,qp5 in order : ".$result->output );
654 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output );
655
656 # The fragment is passed as another parameter.
657 # During the server redirects the fragment will be set to its value, if such a key is present.
658 # 'ebiil' => 'fcjjm' => 'gdkkn' => 'hello'
659 $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;fragment=ebiil?qp1=0' --onredirect=follow -vvv";
660 $result = NPTest->testCmd( "$cmd" );
661 is( $result->return_code, 0, $cmd);
662 like( $result->output, '/.*redirect_count=3;fragment=hello\?qp1=3#hello*/', "Output correct, fragments are specified by server and followed by check_curl: ".$result->output );
663 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output );
664
485 # These tests may block 665 # These tests may block
486 # stickyport - on full urlS port is set back to 80 otherwise 666 # stickyport - on full urlS port is set back to 80 otherwise
487 $cmd = "$command -f stickyport -u /redir_external -t 5 -s redirected"; 667 $cmd = "$command -f stickyport -u /redir_external -t 5 -s redirected";