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 long l;
190
191 if(strcmp(r->handler, "curltest-echo")) {
192 return DECLINED;
193 }
194 if(r->method_number != M_GET && r->method_number != M_POST) {
195 return DECLINED;
196 }
197
198 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "echo_handler: processing");
199 if(r->args) {
200 apr_array_header_t *args = NULL;
201 int i;
202 args = apr_cstr_split(r->args, "&", 1, r->pool);
203 for(i = 0; i < args->nelts; ++i) {
204 char *s, *val, *arg = APR_ARRAY_IDX(args, i, char*);
205 s = strchr(arg, '=');
206 if(s) {
207 *s = '\0';
208 val = s + 1;
209 if(!strcmp("die_after", arg)) {
210 die_after_len = (apr_off_t)apr_atoi64(val);
211 }
212 }
213 }
214 }
215
216 r->status = 200;
217 if(die_after_len >= 0) {
218 r->clength = die_after_len + 1;
219 r->chunked = 0;
220 apr_table_set(r->headers_out, "Content-Length", apr_ltoa(r->pool, (long)r->clength));
221 }
222 else {
223 r->clength = -1;
224 r->chunked = 1;
225 apr_table_unset(r->headers_out, "Content-Length");
226 }
227 /* Discourage content-encodings */
228 apr_table_unset(r->headers_out, "Content-Encoding");
229 apr_table_setn(r->subprocess_env, "no-brotli", "1");
230 apr_table_setn(r->subprocess_env, "no-gzip", "1");
231
232 ct = apr_table_get(r->headers_in, "content-type");
233 ap_set_content_type(r, ct? ct : "application/octet-stream");
234
235 bb = apr_brigade_create(r->pool, c->bucket_alloc);
236 /* copy any request body into the response */
237 if((rv = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK))) goto cleanup;
238 if(ap_should_client_block(r)) {
239 while(0 < (l = ap_get_client_block(r, &buffer[0], sizeof(buffer)))) {
240 total_read_len += l;
241 if(die_after_len >= 0 && total_read_len >= die_after_len) {
242 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
243 "echo_handler: dying after %ld bytes as requested",
244 (long)total_read_len);
245 r->connection->keepalive = AP_CONN_CLOSE;
246 return DONE;
247 }
248 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
249 "echo_handler: copying %ld bytes from request body", l);
250 rv = apr_brigade_write(bb, NULL, NULL, buffer, l);
251 if (APR_SUCCESS != rv) goto cleanup;
252 rv = ap_pass_brigade(r->output_filters, bb);
253 if (APR_SUCCESS != rv) goto cleanup;
254 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
255 "echo_handler: passed %ld bytes from request body", l);
256 }
257 }
258 /* we are done */
259 b = apr_bucket_eos_create(c->bucket_alloc);
260 APR_BRIGADE_INSERT_TAIL(bb, b);
261 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "echo_handler: request read");
262
263 if(r->trailers_in && !apr_is_empty_table(r->trailers_in)) {
264 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
265 "echo_handler: seeing incoming trailers");
266 apr_table_setn(r->trailers_out, "h2test-trailers-in",
267 apr_itoa(r->pool, 1));
268 }
269
270 rv = ap_pass_brigade(r->output_filters, bb);
271
272 cleanup:
273 if(rv == APR_SUCCESS ||
274 r->status != HTTP_OK ||
275 c->aborted) {
276 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "echo_handler: done");
277 return OK;
278 }
279 else {
280 /* no way to know what type of error occurred */
281 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "echo_handler failed");
282 return AP_FILTER_ERROR;
283 }
284 return DECLINED;
285 }
286
curltest_tweak_handler(request_rec * r)287 static int curltest_tweak_handler(request_rec *r)
288 {
289 conn_rec *c = r->connection;
290 apr_bucket_brigade *bb;
291 apr_bucket *b;
292 apr_status_t rv;
293 char buffer[16*1024];
294 int i, chunks = 3, error_bucket = 1;
295 size_t chunk_size = sizeof(buffer);
296 const char *request_id = "none";
297 apr_time_t delay = 0, chunk_delay = 0;
298 apr_array_header_t *args = NULL;
299 int http_status = 200;
300 apr_status_t error = APR_SUCCESS, body_error = APR_SUCCESS;
301
302 if(strcmp(r->handler, "curltest-tweak")) {
303 return DECLINED;
304 }
305 if(r->method_number == M_DELETE) {
306 http_status = 204;
307 chunks = 0;
308 }
309 else if(r->method_number != M_GET && r->method_number != M_POST) {
310 return DECLINED;
311 }
312
313 if(r->args) {
314 args = apr_cstr_split(r->args, "&", 1, r->pool);
315 for(i = 0; i < args->nelts; ++i) {
316 char *s, *val, *arg = APR_ARRAY_IDX(args, i, char*);
317 s = strchr(arg, '=');
318 if(s) {
319 *s = '\0';
320 val = s + 1;
321 if(!strcmp("status", arg)) {
322 http_status = (int)apr_atoi64(val);
323 if(http_status > 0) {
324 continue;
325 }
326 }
327 else if(!strcmp("chunks", arg)) {
328 chunks = (int)apr_atoi64(val);
329 if(chunks >= 0) {
330 continue;
331 }
332 }
333 else if(!strcmp("chunk_size", arg)) {
334 chunk_size = (int)apr_atoi64(val);
335 if(chunk_size >= 0) {
336 if(chunk_size > sizeof(buffer)) {
337 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
338 "chunk_size %zu too large", chunk_size);
339 ap_die(HTTP_BAD_REQUEST, r);
340 return OK;
341 }
342 continue;
343 }
344 }
345 else if(!strcmp("id", arg)) {
346 /* just an id for repeated requests with curl's url globbing */
347 request_id = val;
348 continue;
349 }
350 else if(!strcmp("error", arg)) {
351 if(status_from_str(val, &error)) {
352 continue;
353 }
354 }
355 else if(!strcmp("error_bucket", arg)) {
356 error_bucket = (int)apr_atoi64(val);
357 if(error_bucket >= 0) {
358 continue;
359 }
360 }
361 else if(!strcmp("body_error", arg)) {
362 if(status_from_str(val, &body_error)) {
363 continue;
364 }
365 }
366 else if(!strcmp("delay", arg)) {
367 rv = duration_parse(&delay, val, "s");
368 if(APR_SUCCESS == rv) {
369 continue;
370 }
371 }
372 else if(!strcmp("chunk_delay", arg)) {
373 rv = duration_parse(&chunk_delay, val, "s");
374 if(APR_SUCCESS == rv) {
375 continue;
376 }
377 }
378 }
379 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not "
380 "understood: '%s' in %s",
381 arg, r->args);
382 ap_die(HTTP_BAD_REQUEST, r);
383 return OK;
384 }
385 }
386
387 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "error_handler: processing "
388 "request, %s", r->args? r->args : "(no args)");
389 r->status = http_status;
390 r->clength = -1;
391 r->chunked = (r->proto_num >= HTTP_VERSION(1,1));
392 apr_table_setn(r->headers_out, "request-id", request_id);
393 apr_table_unset(r->headers_out, "Content-Length");
394 /* Discourage content-encodings */
395 apr_table_unset(r->headers_out, "Content-Encoding");
396 apr_table_setn(r->subprocess_env, "no-brotli", "1");
397 apr_table_setn(r->subprocess_env, "no-gzip", "1");
398
399 ap_set_content_type(r, "application/octet-stream");
400 bb = apr_brigade_create(r->pool, c->bucket_alloc);
401
402 if(delay) {
403 apr_sleep(delay);
404 }
405 if(error != APR_SUCCESS) {
406 return ap_map_http_request_error(error, HTTP_BAD_REQUEST);
407 }
408 /* flush response */
409 b = apr_bucket_flush_create(c->bucket_alloc);
410 APR_BRIGADE_INSERT_TAIL(bb, b);
411 rv = ap_pass_brigade(r->output_filters, bb);
412 if (APR_SUCCESS != rv) goto cleanup;
413
414 memset(buffer, 'X', sizeof(buffer));
415 for(i = 0; i < chunks; ++i) {
416 if(chunk_delay) {
417 apr_sleep(chunk_delay);
418 }
419 rv = apr_brigade_write(bb, NULL, NULL, buffer, chunk_size);
420 if(APR_SUCCESS != rv) goto cleanup;
421 rv = ap_pass_brigade(r->output_filters, bb);
422 if(APR_SUCCESS != rv) goto cleanup;
423 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
424 "error_handler: passed %lu bytes as response body",
425 (unsigned long)chunk_size);
426 if(body_error != APR_SUCCESS) {
427 rv = body_error;
428 goto cleanup;
429 }
430 }
431 /* we are done */
432 b = apr_bucket_eos_create(c->bucket_alloc);
433 APR_BRIGADE_INSERT_TAIL(bb, b);
434 rv = ap_pass_brigade(r->output_filters, bb);
435 apr_brigade_cleanup(bb);
436 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r,
437 "error_handler: response passed");
438
439 cleanup:
440 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r,
441 "error_handler: request cleanup, r->status=%d, aborted=%d",
442 r->status, c->aborted);
443 if(rv == APR_SUCCESS) {
444 return OK;
445 }
446 if(error_bucket) {
447 http_status = ap_map_http_request_error(rv, HTTP_BAD_REQUEST);
448 b = ap_bucket_error_create(http_status, NULL, r->pool, c->bucket_alloc);
449 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r,
450 "error_handler: passing error bucket, status=%d",
451 http_status);
452 APR_BRIGADE_INSERT_TAIL(bb, b);
453 ap_pass_brigade(r->output_filters, bb);
454 }
455 return AP_FILTER_ERROR;
456 }
457
curltest_put_handler(request_rec * r)458 static int curltest_put_handler(request_rec *r)
459 {
460 conn_rec *c = r->connection;
461 apr_bucket_brigade *bb;
462 apr_bucket *b;
463 apr_status_t rv;
464 char buffer[16*1024];
465 const char *ct;
466 apr_off_t rbody_len = 0;
467 const char *s_rbody_len;
468 const char *request_id = "none";
469 apr_time_t read_delay = 0, chunk_delay = 0;
470 apr_array_header_t *args = NULL;
471 long l;
472 int i;
473
474 if(strcmp(r->handler, "curltest-put")) {
475 return DECLINED;
476 }
477 if(r->method_number != M_PUT) {
478 return DECLINED;
479 }
480
481 if(r->args) {
482 args = apr_cstr_split(r->args, "&", 1, r->pool);
483 for(i = 0; i < args->nelts; ++i) {
484 char *s, *val, *arg = APR_ARRAY_IDX(args, i, char*);
485 s = strchr(arg, '=');
486 if(s) {
487 *s = '\0';
488 val = s + 1;
489 if(!strcmp("id", arg)) {
490 /* just an id for repeated requests with curl's url globbing */
491 request_id = val;
492 continue;
493 }
494 else if(!strcmp("read_delay", arg)) {
495 rv = duration_parse(&read_delay, val, "s");
496 if(APR_SUCCESS == rv) {
497 continue;
498 }
499 }
500 else if(!strcmp("chunk_delay", arg)) {
501 rv = duration_parse(&chunk_delay, val, "s");
502 if(APR_SUCCESS == rv) {
503 continue;
504 }
505 }
506 }
507 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not "
508 "understood: '%s' in %s",
509 arg, r->args);
510 ap_die(HTTP_BAD_REQUEST, r);
511 return OK;
512 }
513 }
514
515 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "put_handler: processing");
516 r->status = 200;
517 r->clength = -1;
518 r->chunked = 1;
519 apr_table_unset(r->headers_out, "Content-Length");
520 /* Discourage content-encodings */
521 apr_table_unset(r->headers_out, "Content-Encoding");
522 apr_table_setn(r->subprocess_env, "no-brotli", "1");
523 apr_table_setn(r->subprocess_env, "no-gzip", "1");
524
525 ct = apr_table_get(r->headers_in, "content-type");
526 ap_set_content_type(r, ct? ct : "text/plain");
527
528 if(read_delay) {
529 apr_sleep(read_delay);
530 }
531 bb = apr_brigade_create(r->pool, c->bucket_alloc);
532 /* copy any request body into the response */
533 if((rv = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK))) goto cleanup;
534 if(ap_should_client_block(r)) {
535 while(0 < (l = ap_get_client_block(r, &buffer[0], sizeof(buffer)))) {
536 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
537 "put_handler: read %ld bytes from request body", l);
538 if(chunk_delay) {
539 apr_sleep(chunk_delay);
540 }
541 rbody_len += l;
542 }
543 }
544 /* we are done */
545 s_rbody_len = apr_psprintf(r->pool, "%"APR_OFF_T_FMT, rbody_len);
546 apr_table_setn(r->headers_out, "Received-Length", s_rbody_len);
547 rv = apr_brigade_puts(bb, NULL, NULL, s_rbody_len);
548 if(APR_SUCCESS != rv) goto cleanup;
549 b = apr_bucket_eos_create(c->bucket_alloc);
550 APR_BRIGADE_INSERT_TAIL(bb, b);
551 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "put_handler: request read");
552
553 rv = ap_pass_brigade(r->output_filters, bb);
554
555 cleanup:
556 if(rv == APR_SUCCESS
557 || r->status != HTTP_OK
558 || c->aborted) {
559 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "put_handler: done");
560 return OK;
561 }
562 else {
563 /* no way to know what type of error occurred */
564 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "put_handler failed");
565 return AP_FILTER_ERROR;
566 }
567 return DECLINED;
568 }
569
curltest_1_1_required(request_rec * r)570 static int curltest_1_1_required(request_rec *r)
571 {
572 conn_rec *c = r->connection;
573 apr_bucket_brigade *bb;
574 apr_bucket *b;
575 apr_status_t rv;
576 char buffer[16*1024];
577 const char *ct;
578 const char *request_id = "none";
579 apr_time_t chunk_delay = 0;
580 apr_array_header_t *args = NULL;
581 long l;
582 int i;
583
584 if(strcmp(r->handler, "curltest-1_1-required")) {
585 return DECLINED;
586 }
587
588 if (HTTP_VERSION_MAJOR(r->proto_num) > 1) {
589 apr_table_setn(r->notes, "ssl-renegotiate-forbidden", "1");
590 ap_die(HTTP_FORBIDDEN, r);
591 return OK;
592 }
593
594 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "1_1_handler: processing");
595 r->status = 200;
596 r->clength = -1;
597 r->chunked = 1;
598 apr_table_unset(r->headers_out, "Content-Length");
599 /* Discourage content-encodings */
600 apr_table_unset(r->headers_out, "Content-Encoding");
601 apr_table_setn(r->subprocess_env, "no-brotli", "1");
602 apr_table_setn(r->subprocess_env, "no-gzip", "1");
603
604 ct = apr_table_get(r->headers_in, "content-type");
605 ap_set_content_type(r, ct? ct : "text/plain");
606
607 bb = apr_brigade_create(r->pool, c->bucket_alloc);
608 /* flush response */
609 b = apr_bucket_flush_create(c->bucket_alloc);
610 APR_BRIGADE_INSERT_TAIL(bb, b);
611 rv = ap_pass_brigade(r->output_filters, bb);
612 if (APR_SUCCESS != rv) goto cleanup;
613
614 /* we are done */
615 rv = apr_brigade_printf(bb, NULL, NULL, "well done!");
616 if(APR_SUCCESS != rv) goto cleanup;
617 b = apr_bucket_eos_create(c->bucket_alloc);
618 APR_BRIGADE_INSERT_TAIL(bb, b);
619 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "1_1_handler: request read");
620
621 rv = ap_pass_brigade(r->output_filters, bb);
622
623 cleanup:
624 if(rv == APR_SUCCESS
625 || r->status != HTTP_OK
626 || c->aborted) {
627 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler: done");
628 return OK;
629 }
630 else {
631 /* no way to know what type of error occurred */
632 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler failed");
633 return AP_FILTER_ERROR;
634 }
635 return DECLINED;
636 }
637
brigade_env_var(request_rec * r,apr_bucket_brigade * bb,const char * name)638 static int brigade_env_var(request_rec *r, apr_bucket_brigade *bb,
639 const char *name)
640 {
641 const char *s;
642 s = apr_table_get(r->subprocess_env, name);
643 if(s)
644 return apr_brigade_printf(bb, NULL, NULL, ",\n \"%s\": \"%s\"", name, s);
645 return 0;
646 }
647
curltest_sslinfo_handler(request_rec * r)648 static int curltest_sslinfo_handler(request_rec *r)
649 {
650 conn_rec *c = r->connection;
651 apr_bucket_brigade *bb;
652 apr_bucket *b;
653 apr_status_t rv;
654 apr_array_header_t *args = NULL;
655 const char *request_id = NULL;
656 int close_conn = 0;
657 long l;
658 int i;
659
660 if(strcmp(r->handler, "curltest-sslinfo")) {
661 return DECLINED;
662 }
663 if(r->method_number != M_GET) {
664 return DECLINED;
665 }
666
667 if(r->args) {
668 apr_array_header_t *args = apr_cstr_split(r->args, "&", 1, r->pool);
669 for(i = 0; i < args->nelts; ++i) {
670 char *s, *val, *arg = APR_ARRAY_IDX(args, i, char*);
671 s = strchr(arg, '=');
672 if(s) {
673 *s = '\0';
674 val = s + 1;
675 if(!strcmp("id", arg)) {
676 /* just an id for repeated requests with curl's url globbing */
677 request_id = val;
678 continue;
679 }
680 }
681 else if(!strcmp("close", arg)) {
682 /* we are asked to close the connection */
683 close_conn = 1;
684 continue;
685 }
686 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not "
687 "understood: '%s' in %s",
688 arg, r->args);
689 ap_die(HTTP_BAD_REQUEST, r);
690 return OK;
691 }
692 }
693
694 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "sslinfo: processing");
695 r->status = 200;
696 r->clength = -1;
697 r->chunked = 1;
698 apr_table_unset(r->headers_out, "Content-Length");
699 /* Discourage content-encodings */
700 apr_table_unset(r->headers_out, "Content-Encoding");
701 apr_table_setn(r->subprocess_env, "no-brotli", "1");
702 apr_table_setn(r->subprocess_env, "no-gzip", "1");
703
704 ap_set_content_type(r, "application/json");
705
706 bb = apr_brigade_create(r->pool, c->bucket_alloc);
707
708 apr_brigade_puts(bb, NULL, NULL, "{\n \"Name\": \"SSL-Information\"");
709 brigade_env_var(r, bb, "HTTPS");
710 brigade_env_var(r, bb, "SSL_PROTOCOL");
711 brigade_env_var(r, bb, "SSL_CIPHER");
712 brigade_env_var(r, bb, "SSL_SESSION_ID");
713 brigade_env_var(r, bb, "SSL_SESSION_RESUMED");
714 brigade_env_var(r, bb, "SSL_SRP_USER");
715 brigade_env_var(r, bb, "SSL_SRP_USERINFO");
716 brigade_env_var(r, bb, "SSL_TLS_SNI");
717 apr_brigade_puts(bb, NULL, NULL, "}\n");
718
719 /* flush response */
720 b = apr_bucket_flush_create(c->bucket_alloc);
721 APR_BRIGADE_INSERT_TAIL(bb, b);
722 rv = ap_pass_brigade(r->output_filters, bb);
723 if (APR_SUCCESS != rv) goto cleanup;
724
725 /* we are done */
726 b = apr_bucket_eos_create(c->bucket_alloc);
727 APR_BRIGADE_INSERT_TAIL(bb, b);
728 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "1_1_handler: request read");
729
730 rv = ap_pass_brigade(r->output_filters, bb);
731
732 cleanup:
733 if(close_conn)
734 r->connection->keepalive = AP_CONN_CLOSE;
735 if(rv == APR_SUCCESS
736 || r->status != HTTP_OK
737 || c->aborted) {
738 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler: done");
739 return OK;
740 }
741 else {
742 /* no way to know what type of error occurred */
743 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler failed");
744 return AP_FILTER_ERROR;
745 }
746 return DECLINED;
747 }
748