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