1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24 #include <assert.h>
25
26 #include <apr_optional.h>
27 #include <apr_optional_hooks.h>
28 #include <apr_strings.h>
29 #include <apr_cstr.h>
30 #include <apr_time.h>
31 #include <apr_want.h>
32
33 #include <httpd.h>
34 #include <http_protocol.h>
35 #include <http_request.h>
36 #include <http_log.h>
37
38 static void curltest_hooks(apr_pool_t *pool);
39 static int curltest_echo_handler(request_rec *r);
40 static int curltest_put_handler(request_rec *r);
41 static int curltest_tweak_handler(request_rec *r);
42 static int curltest_1_1_required(request_rec *r);
43 static int curltest_sslinfo_handler(request_rec *r);
44
45 AP_DECLARE_MODULE(curltest) =
46 {
47 STANDARD20_MODULE_STUFF,
48 NULL, /* func to create per dir config */
49 NULL, /* func to merge per dir config */
50 NULL, /* func to create per server config */
51 NULL, /* func to merge per server config */
52 NULL, /* command handlers */
53 curltest_hooks,
54 #if defined(AP_MODULE_FLAG_NONE)
55 AP_MODULE_FLAG_ALWAYS_MERGE
56 #endif
57 };
58
curltest_post_config(apr_pool_t * p,apr_pool_t * plog,apr_pool_t * ptemp,server_rec * s)59 static int curltest_post_config(apr_pool_t *p, apr_pool_t *plog,
60 apr_pool_t *ptemp, server_rec *s)
61 {
62 void *data = NULL;
63 const char *key = "mod_curltest_init_counter";
64
65 (void)plog;(void)ptemp;
66
67 apr_pool_userdata_get(&data, key, s->process->pool);
68 if(!data) {
69 /* dry run */
70 apr_pool_userdata_set((const void *)1, key,
71 apr_pool_cleanup_null, s->process->pool);
72 return APR_SUCCESS;
73 }
74
75 /* mess with the overall server here */
76
77 return APR_SUCCESS;
78 }
79
curltest_hooks(apr_pool_t * pool)80 static void curltest_hooks(apr_pool_t *pool)
81 {
82 ap_log_perror(APLOG_MARK, APLOG_TRACE1, 0, pool, "installing hooks");
83
84 /* Run once after configuration is set, but before mpm children initialize.
85 */
86 ap_hook_post_config(curltest_post_config, NULL, NULL, APR_HOOK_MIDDLE);
87
88 /* curl test handlers */
89 ap_hook_handler(curltest_echo_handler, NULL, NULL, APR_HOOK_MIDDLE);
90 ap_hook_handler(curltest_put_handler, NULL, NULL, APR_HOOK_MIDDLE);
91 ap_hook_handler(curltest_tweak_handler, NULL, NULL, APR_HOOK_MIDDLE);
92 ap_hook_handler(curltest_1_1_required, NULL, NULL, APR_HOOK_MIDDLE);
93 ap_hook_handler(curltest_sslinfo_handler, NULL, NULL, APR_HOOK_MIDDLE);
94 }
95
96 #define SECS_PER_HOUR (60*60)
97 #define SECS_PER_DAY (24*SECS_PER_HOUR)
98
duration_parse(apr_interval_time_t * ptimeout,const char * value,const char * def_unit)99 static apr_status_t duration_parse(apr_interval_time_t *ptimeout,
100 const char *value, const char *def_unit)
101 {
102 char *endp;
103 apr_int64_t n;
104
105 n = apr_strtoi64(value, &endp, 10);
106 if(errno) {
107 return errno;
108 }
109 if(!endp || !*endp) {
110 if(!def_unit)
111 def_unit = "s";
112 }
113 else if(endp == value) {
114 return APR_EINVAL;
115 }
116 else {
117 def_unit = endp;
118 }
119
120 switch(*def_unit) {
121 case 'D':
122 case 'd':
123 *ptimeout = apr_time_from_sec(n * SECS_PER_DAY);
124 break;
125 case 's':
126 case 'S':
127 *ptimeout = (apr_interval_time_t) apr_time_from_sec(n);
128 break;
129 case 'h':
130 case 'H':
131 /* Time is in hours */
132 *ptimeout = (apr_interval_time_t) apr_time_from_sec(n * SECS_PER_HOUR);
133 break;
134 case 'm':
135 case 'M':
136 switch(*(++def_unit)) {
137 /* Time is in milliseconds */
138 case 's':
139 case 'S':
140 *ptimeout = (apr_interval_time_t) n * 1000;
141 break;
142 /* Time is in minutes */
143 case 'i':
144 case 'I':
145 *ptimeout = (apr_interval_time_t) apr_time_from_sec(n * 60);
146 break;
147 default:
148 return APR_EGENERAL;
149 }
150 break;
151 case 'u':
152 case 'U':
153 switch(*(++def_unit)) {
154 /* Time is in microseconds */
155 case 's':
156 case 'S':
157 *ptimeout = (apr_interval_time_t) n;
158 break;
159 default:
160 return APR_EGENERAL;
161 }
162 break;
163 default:
164 return APR_EGENERAL;
165 }
166 return APR_SUCCESS;
167 }
168
status_from_str(const char * s,apr_status_t * pstatus)169 static int status_from_str(const char *s, apr_status_t *pstatus)
170 {
171 if(!strcmp("timeout", s)) {
172 *pstatus = APR_TIMEUP;
173 return 1;
174 }
175 else if(!strcmp("reset", s)) {
176 *pstatus = APR_ECONNRESET;
177 return 1;
178 }
179 return 0;
180 }
181
curltest_echo_handler(request_rec * r)182 static int curltest_echo_handler(request_rec *r)
183 {
184 conn_rec *c = r->connection;
185 apr_bucket_brigade *bb;
186 apr_bucket *b;
187 apr_status_t rv;
188 char buffer[8192];
189 const char *ct;
190 apr_off_t die_after_len = -1, total_read_len = 0;
191 int just_die = 0, die_after_100 = 0;
192 long l;
193
194 if(strcmp(r->handler, "curltest-echo")) {
195 return DECLINED;
196 }
197 if(r->method_number != M_GET && r->method_number != M_POST) {
198 return DECLINED;
199 }
200
201 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "echo_handler: processing");
202 if(r->args) {
203 apr_array_header_t *args = NULL;
204 int i;
205 args = apr_cstr_split(r->args, "&", 1, r->pool);
206 for(i = 0; i < args->nelts; ++i) {
207 char *s, *val, *arg = APR_ARRAY_IDX(args, i, char *);
208 s = strchr(arg, '=');
209 if(s) {
210 *s = '\0';
211 val = s + 1;
212 if(!strcmp("die_after", arg)) {
213 die_after_len = (apr_off_t)apr_atoi64(val);
214 continue;
215 }
216 else if(!strcmp("just_die", arg)) {
217 just_die = 1;
218 continue;
219 }
220 else if(!strcmp("die_after_100", arg)) {
221 die_after_100 = 1;
222 continue;
223 }
224 }
225 }
226 }
227
228 if(just_die) {
229 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
230 "echo_handler: dying right away");
231 /* Generate no HTTP response at all. */
232 ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
233 r->connection->keepalive = AP_CONN_CLOSE;
234 return AP_FILTER_ERROR;
235 }
236
237 r->status = 200;
238 if(die_after_len >= 0) {
239 r->clength = die_after_len + 1;
240 r->chunked = 0;
241 apr_table_set(r->headers_out, "Content-Length",
242 apr_ltoa(r->pool, (long)r->clength));
243 }
244 else {
245 r->clength = -1;
246 r->chunked = 1;
247 apr_table_unset(r->headers_out, "Content-Length");
248 }
249 /* Discourage content-encodings */
250 apr_table_unset(r->headers_out, "Content-Encoding");
251 apr_table_setn(r->subprocess_env, "no-brotli", "1");
252 apr_table_setn(r->subprocess_env, "no-gzip", "1");
253
254 ct = apr_table_get(r->headers_in, "content-type");
255 ap_set_content_type(r, ct ? ct : "application/octet-stream");
256
257 bb = apr_brigade_create(r->pool, c->bucket_alloc);
258 /* copy any request body into the response */
259 rv = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
260 if(rv)
261 goto cleanup;
262 if(die_after_100) {
263 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
264 "echo_handler: dying after 100-continue");
265 /* Generate no HTTP response at all. */
266 ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
267 r->connection->keepalive = AP_CONN_CLOSE;
268 return AP_FILTER_ERROR;
269 }
270 if(ap_should_client_block(r)) {
271 while(0 < (l = ap_get_client_block(r, &buffer[0], sizeof(buffer)))) {
272 total_read_len += l;
273 if(die_after_len >= 0 && total_read_len >= die_after_len) {
274 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
275 "echo_handler: dying after %ld bytes as requested",
276 (long)total_read_len);
277 ap_pass_brigade(r->output_filters, bb);
278 ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
279 r->connection->keepalive = AP_CONN_CLOSE;
280 return DONE;
281 }
282 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
283 "echo_handler: copying %ld bytes from request body", l);
284 rv = apr_brigade_write(bb, NULL, NULL, buffer, l);
285 if(APR_SUCCESS != rv)
286 goto cleanup;
287 rv = ap_pass_brigade(r->output_filters, bb);
288 if(APR_SUCCESS != rv)
289 goto cleanup;
290 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
291 "echo_handler: passed %ld bytes from request body", l);
292 }
293 }
294 /* we are done */
295 b = apr_bucket_eos_create(c->bucket_alloc);
296 APR_BRIGADE_INSERT_TAIL(bb, b);
297 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "echo_handler: request read");
298
299 if(r->trailers_in && !apr_is_empty_table(r->trailers_in)) {
300 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
301 "echo_handler: seeing incoming trailers");
302 apr_table_setn(r->trailers_out, "h2test-trailers-in",
303 apr_itoa(r->pool, 1));
304 }
305
306 rv = ap_pass_brigade(r->output_filters, bb);
307
308 cleanup:
309 if(rv == APR_SUCCESS ||
310 r->status != HTTP_OK ||
311 c->aborted) {
312 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "echo_handler: done");
313 return OK;
314 }
315 else {
316 /* no way to know what type of error occurred */
317 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "echo_handler failed");
318 return AP_FILTER_ERROR;
319 }
320 return DECLINED;
321 }
322
curltest_tweak_handler(request_rec * r)323 static int curltest_tweak_handler(request_rec *r)
324 {
325 conn_rec *c = r->connection;
326 apr_bucket_brigade *bb;
327 apr_bucket *b;
328 apr_status_t rv;
329 char buffer[16*1024];
330 int i, chunks = 3, error_bucket = 1;
331 size_t chunk_size = sizeof(buffer);
332 const char *request_id = "none";
333 apr_time_t delay = 0, chunk_delay = 0, close_delay = 0;
334 apr_array_header_t *args = NULL;
335 int http_status = 200;
336 apr_status_t error = APR_SUCCESS, body_error = APR_SUCCESS;
337 int close_conn = 0, with_cl = 0;
338
339 if(strcmp(r->handler, "curltest-tweak")) {
340 return DECLINED;
341 }
342 if(r->method_number == M_DELETE) {
343 http_status = 204;
344 chunks = 0;
345 }
346 else if(r->method_number != M_GET && r->method_number != M_POST) {
347 return DECLINED;
348 }
349
350 if(r->args) {
351 args = apr_cstr_split(r->args, "&", 1, r->pool);
352 for(i = 0; i < args->nelts; ++i) {
353 char *s, *val, *arg = APR_ARRAY_IDX(args, i, char *);
354 s = strchr(arg, '=');
355 if(s) {
356 *s = '\0';
357 val = s + 1;
358 if(!strcmp("status", arg)) {
359 http_status = (int)apr_atoi64(val);
360 if(http_status > 0) {
361 continue;
362 }
363 }
364 else if(!strcmp("chunks", arg)) {
365 chunks = (int)apr_atoi64(val);
366 if(chunks >= 0) {
367 continue;
368 }
369 }
370 else if(!strcmp("chunk_size", arg)) {
371 chunk_size = (int)apr_atoi64(val);
372 if(chunk_size >= 0) {
373 if(chunk_size > sizeof(buffer)) {
374 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
375 "chunk_size %zu too large", chunk_size);
376 ap_die(HTTP_BAD_REQUEST, r);
377 return OK;
378 }
379 continue;
380 }
381 }
382 else if(!strcmp("id", arg)) {
383 /* just an id for repeated requests with curl's url globbing */
384 request_id = val;
385 continue;
386 }
387 else if(!strcmp("error", arg)) {
388 if(status_from_str(val, &error)) {
389 continue;
390 }
391 }
392 else if(!strcmp("error_bucket", arg)) {
393 error_bucket = (int)apr_atoi64(val);
394 if(error_bucket >= 0) {
395 continue;
396 }
397 }
398 else if(!strcmp("body_error", arg)) {
399 if(status_from_str(val, &body_error)) {
400 continue;
401 }
402 }
403 else if(!strcmp("delay", arg)) {
404 rv = duration_parse(&delay, val, "s");
405 if(APR_SUCCESS == rv) {
406 continue;
407 }
408 }
409 else if(!strcmp("chunk_delay", arg)) {
410 rv = duration_parse(&chunk_delay, val, "s");
411 if(APR_SUCCESS == rv) {
412 continue;
413 }
414 }
415 else if(!strcmp("close_delay", arg)) {
416 rv = duration_parse(&close_delay, val, "s");
417 if(APR_SUCCESS == rv) {
418 continue;
419 }
420 }
421 }
422 else if(!strcmp("close", arg)) {
423 /* we are asked to close the connection */
424 close_conn = 1;
425 continue;
426 }
427 else if(!strcmp("with_cl", arg)) {
428 with_cl = 1;
429 continue;
430 }
431 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not "
432 "understood: '%s' in %s",
433 arg, r->args);
434 ap_die(HTTP_BAD_REQUEST, r);
435 return OK;
436 }
437 }
438
439 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "error_handler: processing "
440 "request, %s", r->args? r->args : "(no args)");
441 r->status = http_status;
442 r->clength = with_cl ? (chunks * chunk_size) : -1;
443 r->chunked = (r->proto_num >= HTTP_VERSION(1, 1)) && !with_cl;
444 apr_table_setn(r->headers_out, "request-id", request_id);
445 if(r->clength >= 0) {
446 apr_table_set(r->headers_out, "Content-Length",
447 apr_ltoa(r->pool, (long)r->clength));
448 }
449 else
450 apr_table_unset(r->headers_out, "Content-Length");
451 /* Discourage content-encodings */
452 apr_table_unset(r->headers_out, "Content-Encoding");
453 apr_table_setn(r->subprocess_env, "no-brotli", "1");
454 apr_table_setn(r->subprocess_env, "no-gzip", "1");
455
456 ap_set_content_type(r, "application/octet-stream");
457 bb = apr_brigade_create(r->pool, c->bucket_alloc);
458
459 if(delay) {
460 apr_sleep(delay);
461 }
462 if(error != APR_SUCCESS) {
463 return ap_map_http_request_error(error, HTTP_BAD_REQUEST);
464 }
465 /* flush response */
466 b = apr_bucket_flush_create(c->bucket_alloc);
467 APR_BRIGADE_INSERT_TAIL(bb, b);
468 rv = ap_pass_brigade(r->output_filters, bb);
469 if(APR_SUCCESS != rv)
470 goto cleanup;
471
472 memset(buffer, 'X', sizeof(buffer));
473 for(i = 0; i < chunks; ++i) {
474 if(chunk_delay) {
475 apr_sleep(chunk_delay);
476 }
477 rv = apr_brigade_write(bb, NULL, NULL, buffer, chunk_size);
478 if(APR_SUCCESS != rv)
479 goto cleanup;
480 rv = ap_pass_brigade(r->output_filters, bb);
481 if(APR_SUCCESS != rv)
482 goto cleanup;
483 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
484 "error_handler: passed %lu bytes as response body",
485 (unsigned long)chunk_size);
486 if(body_error != APR_SUCCESS) {
487 rv = body_error;
488 goto cleanup;
489 }
490 }
491 /* we are done */
492 b = apr_bucket_eos_create(c->bucket_alloc);
493 APR_BRIGADE_INSERT_TAIL(bb, b);
494 rv = ap_pass_brigade(r->output_filters, bb);
495 apr_brigade_cleanup(bb);
496 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r,
497 "error_handler: response passed");
498
499 cleanup:
500 if(close_conn) {
501 if(close_delay) {
502 b = apr_bucket_flush_create(c->bucket_alloc);
503 APR_BRIGADE_INSERT_TAIL(bb, b);
504 rv = ap_pass_brigade(r->output_filters, bb);
505 apr_brigade_cleanup(bb);
506 apr_sleep(close_delay);
507 }
508 r->connection->keepalive = AP_CONN_CLOSE;
509 }
510 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r,
511 "error_handler: request cleanup, r->status=%d, aborted=%d, "
512 "close=%d", r->status, c->aborted, close_conn);
513 if(rv == APR_SUCCESS) {
514 return OK;
515 }
516 if(error_bucket) {
517 http_status = ap_map_http_request_error(rv, HTTP_BAD_REQUEST);
518 b = ap_bucket_error_create(http_status, NULL, r->pool, c->bucket_alloc);
519 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r,
520 "error_handler: passing error bucket, status=%d",
521 http_status);
522 APR_BRIGADE_INSERT_TAIL(bb, b);
523 ap_pass_brigade(r->output_filters, bb);
524 }
525 return AP_FILTER_ERROR;
526 }
527
curltest_put_handler(request_rec * r)528 static int curltest_put_handler(request_rec *r)
529 {
530 conn_rec *c = r->connection;
531 apr_bucket_brigade *bb;
532 apr_bucket *b;
533 apr_status_t rv;
534 char buffer[128*1024];
535 const char *ct;
536 apr_off_t rbody_len = 0;
537 apr_off_t rbody_max_len = -1;
538 const char *s_rbody_len;
539 const char *request_id = "none";
540 apr_time_t read_delay = 0, chunk_delay = 0;
541 apr_array_header_t *args = NULL;
542 long l;
543 int i;
544
545 if(strcmp(r->handler, "curltest-put")) {
546 return DECLINED;
547 }
548 if(r->method_number != M_PUT) {
549 return DECLINED;
550 }
551
552 if(r->args) {
553 args = apr_cstr_split(r->args, "&", 1, r->pool);
554 for(i = 0; i < args->nelts; ++i) {
555 char *s, *val, *arg = APR_ARRAY_IDX(args, i, char *);
556 s = strchr(arg, '=');
557 if(s) {
558 *s = '\0';
559 val = s + 1;
560 if(!strcmp("id", arg)) {
561 /* just an id for repeated requests with curl's url globbing */
562 request_id = val;
563 continue;
564 }
565 else if(!strcmp("read_delay", arg)) {
566 rv = duration_parse(&read_delay, val, "s");
567 if(APR_SUCCESS == rv) {
568 continue;
569 }
570 }
571 else if(!strcmp("chunk_delay", arg)) {
572 rv = duration_parse(&chunk_delay, val, "s");
573 if(APR_SUCCESS == rv) {
574 continue;
575 }
576 }
577 else if(!strcmp("max_upload", arg)) {
578 rbody_max_len = (int)apr_atoi64(val);
579 continue;
580 }
581 }
582 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not "
583 "understood: '%s' in %s",
584 arg, r->args);
585 ap_die(HTTP_BAD_REQUEST, r);
586 return OK;
587 }
588 }
589
590 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "put_handler: processing");
591 r->status = 200;
592 r->clength = -1;
593 r->chunked = 1;
594 apr_table_unset(r->headers_out, "Content-Length");
595 /* Discourage content-encodings */
596 apr_table_unset(r->headers_out, "Content-Encoding");
597 apr_table_setn(r->subprocess_env, "no-brotli", "1");
598 apr_table_setn(r->subprocess_env, "no-gzip", "1");
599
600 ct = apr_table_get(r->headers_in, "content-type");
601 ap_set_content_type(r, ct ? ct : "text/plain");
602
603 if(read_delay) {
604 apr_sleep(read_delay);
605 }
606 bb = apr_brigade_create(r->pool, c->bucket_alloc);
607 /* copy any request body into the response */
608 rv = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
609 if(rv)
610 goto cleanup;
611 if(ap_should_client_block(r)) {
612 while(0 < (l = ap_get_client_block(r, &buffer[0], sizeof(buffer)))) {
613 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
614 "put_handler: read %ld bytes from request body", l);
615 if(chunk_delay) {
616 apr_sleep(chunk_delay);
617 }
618 rbody_len += l;
619 if((rbody_max_len > 0) && (rbody_len > rbody_max_len)) {
620 r->status = 413;
621 break;
622 }
623 }
624 }
625 /* we are done */
626 s_rbody_len = apr_psprintf(r->pool, "%"APR_OFF_T_FMT, rbody_len);
627 apr_table_setn(r->headers_out, "Received-Length", s_rbody_len);
628 rv = apr_brigade_puts(bb, NULL, NULL, s_rbody_len);
629 if(APR_SUCCESS != rv)
630 goto cleanup;
631 b = apr_bucket_eos_create(c->bucket_alloc);
632 APR_BRIGADE_INSERT_TAIL(bb, b);
633 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "put_handler: request read");
634
635 rv = ap_pass_brigade(r->output_filters, bb);
636
637 if(r->status == 413) {
638 apr_sleep(apr_time_from_sec(1));
639 }
640
641 cleanup:
642 if(rv == APR_SUCCESS
643 || r->status != HTTP_OK
644 || c->aborted) {
645 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "put_handler: done");
646 return OK;
647 }
648 else {
649 /* no way to know what type of error occurred */
650 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "put_handler failed");
651 return AP_FILTER_ERROR;
652 }
653 return DECLINED;
654 }
655
curltest_1_1_required(request_rec * r)656 static int curltest_1_1_required(request_rec *r)
657 {
658 conn_rec *c = r->connection;
659 apr_bucket_brigade *bb;
660 apr_bucket *b;
661 apr_status_t rv;
662 char buffer[16*1024];
663 const char *ct;
664 const char *request_id = "none";
665 apr_time_t chunk_delay = 0;
666 apr_array_header_t *args = NULL;
667 long l;
668 int i;
669
670 if(strcmp(r->handler, "curltest-1_1-required")) {
671 return DECLINED;
672 }
673
674 if(HTTP_VERSION_MAJOR(r->proto_num) > 1) {
675 apr_table_setn(r->notes, "ssl-renegotiate-forbidden", "1");
676 ap_die(HTTP_FORBIDDEN, r);
677 return OK;
678 }
679
680 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "1_1_handler: processing");
681 r->status = 200;
682 r->clength = -1;
683 r->chunked = 1;
684 apr_table_unset(r->headers_out, "Content-Length");
685 /* Discourage content-encodings */
686 apr_table_unset(r->headers_out, "Content-Encoding");
687 apr_table_setn(r->subprocess_env, "no-brotli", "1");
688 apr_table_setn(r->subprocess_env, "no-gzip", "1");
689
690 ct = apr_table_get(r->headers_in, "content-type");
691 ap_set_content_type(r, ct ? ct : "text/plain");
692
693 bb = apr_brigade_create(r->pool, c->bucket_alloc);
694 /* flush response */
695 b = apr_bucket_flush_create(c->bucket_alloc);
696 APR_BRIGADE_INSERT_TAIL(bb, b);
697 rv = ap_pass_brigade(r->output_filters, bb);
698 if(APR_SUCCESS != rv)
699 goto cleanup;
700
701 /* we are done */
702 rv = apr_brigade_printf(bb, NULL, NULL, "well done!");
703 if(APR_SUCCESS != rv)
704 goto cleanup;
705 b = apr_bucket_eos_create(c->bucket_alloc);
706 APR_BRIGADE_INSERT_TAIL(bb, b);
707 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "1_1_handler: request read");
708
709 rv = ap_pass_brigade(r->output_filters, bb);
710
711 cleanup:
712 if(rv == APR_SUCCESS
713 || r->status != HTTP_OK
714 || c->aborted) {
715 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler: done");
716 return OK;
717 }
718 else {
719 /* no way to know what type of error occurred */
720 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler failed");
721 return AP_FILTER_ERROR;
722 }
723 return DECLINED;
724 }
725
brigade_env_var(request_rec * r,apr_bucket_brigade * bb,const char * name)726 static int brigade_env_var(request_rec *r, apr_bucket_brigade *bb,
727 const char *name)
728 {
729 const char *s;
730 s = apr_table_get(r->subprocess_env, name);
731 if(s)
732 return apr_brigade_printf(bb, NULL, NULL, ",\n \"%s\": \"%s\"", name, s);
733 return 0;
734 }
735
curltest_sslinfo_handler(request_rec * r)736 static int curltest_sslinfo_handler(request_rec *r)
737 {
738 conn_rec *c = r->connection;
739 apr_bucket_brigade *bb;
740 apr_bucket *b;
741 apr_status_t rv;
742 apr_array_header_t *args = NULL;
743 const char *request_id = NULL;
744 int close_conn = 0;
745 long l;
746 int i;
747
748 if(strcmp(r->handler, "curltest-sslinfo")) {
749 return DECLINED;
750 }
751 if(r->method_number != M_GET) {
752 return DECLINED;
753 }
754
755 if(r->args) {
756 apr_array_header_t *args = apr_cstr_split(r->args, "&", 1, r->pool);
757 for(i = 0; i < args->nelts; ++i) {
758 char *s, *val, *arg = APR_ARRAY_IDX(args, i, char *);
759 s = strchr(arg, '=');
760 if(s) {
761 *s = '\0';
762 val = s + 1;
763 if(!strcmp("id", arg)) {
764 /* just an id for repeated requests with curl's url globbing */
765 request_id = val;
766 continue;
767 }
768 }
769 else if(!strcmp("close", arg)) {
770 /* we are asked to close the connection */
771 close_conn = 1;
772 continue;
773 }
774 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not "
775 "understood: '%s' in %s",
776 arg, r->args);
777 ap_die(HTTP_BAD_REQUEST, r);
778 return OK;
779 }
780 }
781
782 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "sslinfo: processing");
783 r->status = 200;
784 r->clength = -1;
785 r->chunked = 1;
786 apr_table_unset(r->headers_out, "Content-Length");
787 /* Discourage content-encodings */
788 apr_table_unset(r->headers_out, "Content-Encoding");
789 apr_table_setn(r->subprocess_env, "no-brotli", "1");
790 apr_table_setn(r->subprocess_env, "no-gzip", "1");
791
792 ap_set_content_type(r, "application/json");
793
794 bb = apr_brigade_create(r->pool, c->bucket_alloc);
795
796 apr_brigade_puts(bb, NULL, NULL, "{\n \"Name\": \"SSL-Information\"");
797 brigade_env_var(r, bb, "HTTPS");
798 brigade_env_var(r, bb, "SSL_PROTOCOL");
799 brigade_env_var(r, bb, "SSL_CIPHER");
800 brigade_env_var(r, bb, "SSL_SESSION_ID");
801 brigade_env_var(r, bb, "SSL_SESSION_RESUMED");
802 brigade_env_var(r, bb, "SSL_SRP_USER");
803 brigade_env_var(r, bb, "SSL_SRP_USERINFO");
804 brigade_env_var(r, bb, "SSL_TLS_SNI");
805 apr_brigade_puts(bb, NULL, NULL, "}\n");
806
807 /* flush response */
808 b = apr_bucket_flush_create(c->bucket_alloc);
809 APR_BRIGADE_INSERT_TAIL(bb, b);
810 rv = ap_pass_brigade(r->output_filters, bb);
811 if(APR_SUCCESS != rv)
812 goto cleanup;
813
814 /* we are done */
815 b = apr_bucket_eos_create(c->bucket_alloc);
816 APR_BRIGADE_INSERT_TAIL(bb, b);
817 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "1_1_handler: request read");
818
819 rv = ap_pass_brigade(r->output_filters, bb);
820
821 cleanup:
822 if(close_conn)
823 r->connection->keepalive = AP_CONN_CLOSE;
824 if(rv == APR_SUCCESS
825 || r->status != HTTP_OK
826 || c->aborted) {
827 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler: done");
828 return OK;
829 }
830 else {
831 /* no way to know what type of error occurred */
832 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler failed");
833 return AP_FILTER_ERROR;
834 }
835 return DECLINED;
836 }
837