mod_curltest.c (25898B)
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 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 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 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 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 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 if(apr_table_get(r->headers_in, "TE")) 258 apr_table_setn(r->headers_out, "Request-TE", 259 apr_table_get(r->headers_in, "TE")); 260 261 bb = apr_brigade_create(r->pool, c->bucket_alloc); 262 /* copy any request body into the response */ 263 rv = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK); 264 if(rv) 265 goto cleanup; 266 if(die_after_100) { 267 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 268 "echo_handler: dying after 100-continue"); 269 /* Generate no HTTP response at all. */ 270 ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER"); 271 r->connection->keepalive = AP_CONN_CLOSE; 272 return AP_FILTER_ERROR; 273 } 274 if(ap_should_client_block(r)) { 275 while(0 < (l = ap_get_client_block(r, &buffer[0], sizeof(buffer)))) { 276 total_read_len += l; 277 if(die_after_len >= 0 && total_read_len >= die_after_len) { 278 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 279 "echo_handler: dying after %ld bytes as requested", 280 (long)total_read_len); 281 ap_pass_brigade(r->output_filters, bb); 282 ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER"); 283 r->connection->keepalive = AP_CONN_CLOSE; 284 return DONE; 285 } 286 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, 287 "echo_handler: copying %ld bytes from request body", l); 288 rv = apr_brigade_write(bb, NULL, NULL, buffer, l); 289 if(APR_SUCCESS != rv) 290 goto cleanup; 291 rv = ap_pass_brigade(r->output_filters, bb); 292 if(APR_SUCCESS != rv) 293 goto cleanup; 294 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, 295 "echo_handler: passed %ld bytes from request body", l); 296 } 297 } 298 /* we are done */ 299 b = apr_bucket_eos_create(c->bucket_alloc); 300 APR_BRIGADE_INSERT_TAIL(bb, b); 301 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "echo_handler: request read"); 302 303 if(r->trailers_in && !apr_is_empty_table(r->trailers_in)) { 304 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, 305 "echo_handler: seeing incoming trailers"); 306 apr_table_setn(r->trailers_out, "h2test-trailers-in", 307 apr_itoa(r->pool, 1)); 308 } 309 310 rv = ap_pass_brigade(r->output_filters, bb); 311 312 cleanup: 313 if(rv == APR_SUCCESS || 314 r->status != HTTP_OK || 315 c->aborted) { 316 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "echo_handler: done"); 317 return OK; 318 } 319 else { 320 /* no way to know what type of error occurred */ 321 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "echo_handler failed"); 322 return AP_FILTER_ERROR; 323 } 324 return DECLINED; 325 } 326 327 static int curltest_tweak_handler(request_rec *r) 328 { 329 conn_rec *c = r->connection; 330 apr_bucket_brigade *bb; 331 apr_bucket *b; 332 apr_status_t rv; 333 char buffer[16*1024]; 334 int i, chunks = 3, error_bucket = 1; 335 size_t chunk_size = sizeof(buffer); 336 const char *request_id = "none"; 337 apr_time_t delay = 0, chunk_delay = 0, close_delay = 0; 338 apr_array_header_t *args = NULL; 339 int http_status = 200; 340 apr_status_t error = APR_SUCCESS, body_error = APR_SUCCESS; 341 int close_conn = 0, with_cl = 0; 342 int x_hd_len = 0, x_hd1_len = 0; 343 344 if(strcmp(r->handler, "curltest-tweak")) { 345 return DECLINED; 346 } 347 if(r->method_number == M_DELETE) { 348 http_status = 204; 349 chunks = 0; 350 } 351 else if(r->method_number != M_GET && r->method_number != M_POST) { 352 return DECLINED; 353 } 354 355 if(r->args) { 356 args = apr_cstr_split(r->args, "&", 1, r->pool); 357 for(i = 0; i < args->nelts; ++i) { 358 char *s, *val, *arg = APR_ARRAY_IDX(args, i, char *); 359 s = strchr(arg, '='); 360 if(s) { 361 *s = '\0'; 362 val = s + 1; 363 if(!strcmp("status", arg)) { 364 http_status = (int)apr_atoi64(val); 365 if(http_status > 0) { 366 continue; 367 } 368 } 369 else if(!strcmp("chunks", arg)) { 370 chunks = (int)apr_atoi64(val); 371 if(chunks >= 0) { 372 continue; 373 } 374 } 375 else if(!strcmp("chunk_size", arg)) { 376 chunk_size = (int)apr_atoi64(val); 377 if(chunk_size >= 0) { 378 if(chunk_size > sizeof(buffer)) { 379 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 380 "chunk_size %zu too large", chunk_size); 381 ap_die(HTTP_BAD_REQUEST, r); 382 return OK; 383 } 384 continue; 385 } 386 } 387 else if(!strcmp("id", arg)) { 388 /* just an id for repeated requests with curl's url globbing */ 389 request_id = val; 390 continue; 391 } 392 else if(!strcmp("error", arg)) { 393 if(status_from_str(val, &error)) { 394 continue; 395 } 396 } 397 else if(!strcmp("error_bucket", arg)) { 398 error_bucket = (int)apr_atoi64(val); 399 if(error_bucket >= 0) { 400 continue; 401 } 402 } 403 else if(!strcmp("body_error", arg)) { 404 if(status_from_str(val, &body_error)) { 405 continue; 406 } 407 } 408 else if(!strcmp("delay", arg)) { 409 rv = duration_parse(&delay, val, "s"); 410 if(APR_SUCCESS == rv) { 411 continue; 412 } 413 } 414 else if(!strcmp("chunk_delay", arg)) { 415 rv = duration_parse(&chunk_delay, val, "s"); 416 if(APR_SUCCESS == rv) { 417 continue; 418 } 419 } 420 else if(!strcmp("close_delay", arg)) { 421 rv = duration_parse(&close_delay, val, "s"); 422 if(APR_SUCCESS == rv) { 423 continue; 424 } 425 } 426 else if(!strcmp("x-hd", arg)) { 427 x_hd_len = (int)apr_atoi64(val); 428 continue; 429 } 430 else if(!strcmp("x-hd1", arg)) { 431 x_hd1_len = (int)apr_atoi64(val); 432 continue; 433 } 434 } 435 else if(!strcmp("close", arg)) { 436 /* we are asked to close the connection */ 437 close_conn = 1; 438 continue; 439 } 440 else if(!strcmp("with_cl", arg)) { 441 with_cl = 1; 442 continue; 443 } 444 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not " 445 "understood: '%s' in %s", 446 arg, r->args); 447 ap_die(HTTP_BAD_REQUEST, r); 448 return OK; 449 } 450 } 451 452 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "error_handler: processing " 453 "request, %s", r->args? r->args : "(no args)"); 454 r->status = http_status; 455 r->clength = with_cl ? (chunks * chunk_size) : -1; 456 r->chunked = (r->proto_num >= HTTP_VERSION(1, 1)) && !with_cl; 457 apr_table_setn(r->headers_out, "request-id", request_id); 458 if(r->clength >= 0) { 459 apr_table_set(r->headers_out, "Content-Length", 460 apr_ltoa(r->pool, (long)r->clength)); 461 } 462 else 463 apr_table_unset(r->headers_out, "Content-Length"); 464 /* Discourage content-encodings */ 465 apr_table_unset(r->headers_out, "Content-Encoding"); 466 if(x_hd_len > 0) { 467 int i, hd_len = (16 * 1024); 468 int n = (x_hd_len / hd_len); 469 char *hd_val = apr_palloc(r->pool, x_hd_len); 470 memset(hd_val, 'X', hd_len); 471 hd_val[hd_len - 1] = 0; 472 for(i = 0; i < n; ++i) { 473 apr_table_setn(r->headers_out, 474 apr_psprintf(r->pool, "X-Header-%d", i), hd_val); 475 } 476 if(x_hd_len % hd_len) { 477 hd_val[(x_hd_len % hd_len)] = 0; 478 apr_table_setn(r->headers_out, 479 apr_psprintf(r->pool, "X-Header-%d", i), hd_val); 480 } 481 } 482 if(x_hd1_len > 0) { 483 char *hd_val = apr_palloc(r->pool, x_hd1_len); 484 memset(hd_val, 'Y', x_hd1_len); 485 hd_val[x_hd1_len - 1] = 0; 486 apr_table_setn(r->headers_out, "X-Mega-Header", hd_val); 487 } 488 489 apr_table_setn(r->subprocess_env, "no-brotli", "1"); 490 apr_table_setn(r->subprocess_env, "no-gzip", "1"); 491 ap_set_content_type(r, "application/octet-stream"); 492 bb = apr_brigade_create(r->pool, c->bucket_alloc); 493 494 if(delay) { 495 apr_sleep(delay); 496 } 497 if(error != APR_SUCCESS) { 498 return ap_map_http_request_error(error, HTTP_BAD_REQUEST); 499 } 500 /* flush response */ 501 b = apr_bucket_flush_create(c->bucket_alloc); 502 APR_BRIGADE_INSERT_TAIL(bb, b); 503 rv = ap_pass_brigade(r->output_filters, bb); 504 if(APR_SUCCESS != rv) 505 goto cleanup; 506 507 memset(buffer, 'X', sizeof(buffer)); 508 for(i = 0; i < chunks; ++i) { 509 if(chunk_delay) { 510 apr_sleep(chunk_delay); 511 } 512 rv = apr_brigade_write(bb, NULL, NULL, buffer, chunk_size); 513 if(APR_SUCCESS != rv) 514 goto cleanup; 515 rv = ap_pass_brigade(r->output_filters, bb); 516 if(APR_SUCCESS != rv) 517 goto cleanup; 518 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, 519 "error_handler: passed %lu bytes as response body", 520 (unsigned long)chunk_size); 521 if(body_error != APR_SUCCESS) { 522 rv = body_error; 523 goto cleanup; 524 } 525 } 526 /* we are done */ 527 b = apr_bucket_eos_create(c->bucket_alloc); 528 APR_BRIGADE_INSERT_TAIL(bb, b); 529 rv = ap_pass_brigade(r->output_filters, bb); 530 apr_brigade_cleanup(bb); 531 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, 532 "error_handler: response passed"); 533 534 cleanup: 535 if(close_conn) { 536 if(close_delay) { 537 b = apr_bucket_flush_create(c->bucket_alloc); 538 APR_BRIGADE_INSERT_TAIL(bb, b); 539 rv = ap_pass_brigade(r->output_filters, bb); 540 apr_brigade_cleanup(bb); 541 apr_sleep(close_delay); 542 } 543 r->connection->keepalive = AP_CONN_CLOSE; 544 } 545 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, 546 "error_handler: request cleanup, r->status=%d, aborted=%d, " 547 "close=%d", r->status, c->aborted, close_conn); 548 if(rv == APR_SUCCESS) { 549 return OK; 550 } 551 if(error_bucket) { 552 http_status = ap_map_http_request_error(rv, HTTP_BAD_REQUEST); 553 b = ap_bucket_error_create(http_status, NULL, r->pool, c->bucket_alloc); 554 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, 555 "error_handler: passing error bucket, status=%d", 556 http_status); 557 APR_BRIGADE_INSERT_TAIL(bb, b); 558 ap_pass_brigade(r->output_filters, bb); 559 } 560 return AP_FILTER_ERROR; 561 } 562 563 static int curltest_put_handler(request_rec *r) 564 { 565 conn_rec *c = r->connection; 566 apr_bucket_brigade *bb; 567 apr_bucket *b; 568 apr_status_t rv; 569 char buffer[128*1024]; 570 const char *ct; 571 apr_off_t rbody_len = 0; 572 apr_off_t rbody_max_len = -1; 573 const char *s_rbody_len; 574 const char *request_id = "none"; 575 apr_time_t read_delay = 0, chunk_delay = 0; 576 apr_array_header_t *args = NULL; 577 long l; 578 int i; 579 580 if(strcmp(r->handler, "curltest-put")) { 581 return DECLINED; 582 } 583 if(r->method_number != M_PUT) { 584 return DECLINED; 585 } 586 587 if(r->args) { 588 args = apr_cstr_split(r->args, "&", 1, r->pool); 589 for(i = 0; i < args->nelts; ++i) { 590 char *s, *val, *arg = APR_ARRAY_IDX(args, i, char *); 591 s = strchr(arg, '='); 592 if(s) { 593 *s = '\0'; 594 val = s + 1; 595 if(!strcmp("id", arg)) { 596 /* just an id for repeated requests with curl's url globbing */ 597 request_id = val; 598 continue; 599 } 600 else if(!strcmp("read_delay", arg)) { 601 rv = duration_parse(&read_delay, val, "s"); 602 if(APR_SUCCESS == rv) { 603 continue; 604 } 605 } 606 else if(!strcmp("chunk_delay", arg)) { 607 rv = duration_parse(&chunk_delay, val, "s"); 608 if(APR_SUCCESS == rv) { 609 continue; 610 } 611 } 612 else if(!strcmp("max_upload", arg)) { 613 rbody_max_len = (int)apr_atoi64(val); 614 continue; 615 } 616 } 617 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not " 618 "understood: '%s' in %s", 619 arg, r->args); 620 ap_die(HTTP_BAD_REQUEST, r); 621 return OK; 622 } 623 } 624 625 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "put_handler: processing"); 626 r->status = 200; 627 r->clength = -1; 628 r->chunked = 1; 629 apr_table_unset(r->headers_out, "Content-Length"); 630 /* Discourage content-encodings */ 631 apr_table_unset(r->headers_out, "Content-Encoding"); 632 apr_table_setn(r->subprocess_env, "no-brotli", "1"); 633 apr_table_setn(r->subprocess_env, "no-gzip", "1"); 634 635 ct = apr_table_get(r->headers_in, "content-type"); 636 ap_set_content_type(r, ct ? ct : "text/plain"); 637 638 if(read_delay) { 639 apr_sleep(read_delay); 640 } 641 bb = apr_brigade_create(r->pool, c->bucket_alloc); 642 /* copy any request body into the response */ 643 rv = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK); 644 if(rv) 645 goto cleanup; 646 if(ap_should_client_block(r)) { 647 while(0 < (l = ap_get_client_block(r, &buffer[0], sizeof(buffer)))) { 648 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, 649 "put_handler: read %ld bytes from request body", l); 650 if(chunk_delay) { 651 apr_sleep(chunk_delay); 652 } 653 rbody_len += l; 654 if((rbody_max_len > 0) && (rbody_len > rbody_max_len)) { 655 r->status = 413; 656 break; 657 } 658 } 659 } 660 /* we are done */ 661 s_rbody_len = apr_psprintf(r->pool, "%"APR_OFF_T_FMT, rbody_len); 662 apr_table_setn(r->headers_out, "Received-Length", s_rbody_len); 663 rv = apr_brigade_puts(bb, NULL, NULL, s_rbody_len); 664 if(APR_SUCCESS != rv) 665 goto cleanup; 666 b = apr_bucket_eos_create(c->bucket_alloc); 667 APR_BRIGADE_INSERT_TAIL(bb, b); 668 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "put_handler: request read"); 669 670 rv = ap_pass_brigade(r->output_filters, bb); 671 672 if(r->status == 413) { 673 apr_sleep(apr_time_from_sec(1)); 674 } 675 676 cleanup: 677 if(rv == APR_SUCCESS 678 || r->status != HTTP_OK 679 || c->aborted) { 680 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "put_handler: done"); 681 return OK; 682 } 683 else { 684 /* no way to know what type of error occurred */ 685 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "put_handler failed"); 686 return AP_FILTER_ERROR; 687 } 688 return DECLINED; 689 } 690 691 static int curltest_1_1_required(request_rec *r) 692 { 693 conn_rec *c = r->connection; 694 apr_bucket_brigade *bb; 695 apr_bucket *b; 696 apr_status_t rv; 697 char buffer[16*1024]; 698 const char *ct; 699 const char *request_id = "none"; 700 apr_time_t chunk_delay = 0; 701 apr_array_header_t *args = NULL; 702 long l; 703 int i; 704 705 if(strcmp(r->handler, "curltest-1_1-required")) { 706 return DECLINED; 707 } 708 709 if(HTTP_VERSION_MAJOR(r->proto_num) > 1) { 710 apr_table_setn(r->notes, "ssl-renegotiate-forbidden", "1"); 711 ap_die(HTTP_FORBIDDEN, r); 712 return OK; 713 } 714 715 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "1_1_handler: processing"); 716 r->status = 200; 717 r->clength = -1; 718 r->chunked = 1; 719 apr_table_unset(r->headers_out, "Content-Length"); 720 /* Discourage content-encodings */ 721 apr_table_unset(r->headers_out, "Content-Encoding"); 722 apr_table_setn(r->subprocess_env, "no-brotli", "1"); 723 apr_table_setn(r->subprocess_env, "no-gzip", "1"); 724 725 ct = apr_table_get(r->headers_in, "content-type"); 726 ap_set_content_type(r, ct ? ct : "text/plain"); 727 728 bb = apr_brigade_create(r->pool, c->bucket_alloc); 729 /* flush response */ 730 b = apr_bucket_flush_create(c->bucket_alloc); 731 APR_BRIGADE_INSERT_TAIL(bb, b); 732 rv = ap_pass_brigade(r->output_filters, bb); 733 if(APR_SUCCESS != rv) 734 goto cleanup; 735 736 /* we are done */ 737 rv = apr_brigade_printf(bb, NULL, NULL, "well done!"); 738 if(APR_SUCCESS != rv) 739 goto cleanup; 740 b = apr_bucket_eos_create(c->bucket_alloc); 741 APR_BRIGADE_INSERT_TAIL(bb, b); 742 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "1_1_handler: request read"); 743 744 rv = ap_pass_brigade(r->output_filters, bb); 745 746 cleanup: 747 if(rv == APR_SUCCESS 748 || r->status != HTTP_OK 749 || c->aborted) { 750 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler: done"); 751 return OK; 752 } 753 else { 754 /* no way to know what type of error occurred */ 755 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler failed"); 756 return AP_FILTER_ERROR; 757 } 758 return DECLINED; 759 } 760 761 static int brigade_env_var(request_rec *r, apr_bucket_brigade *bb, 762 const char *name) 763 { 764 const char *s; 765 s = apr_table_get(r->subprocess_env, name); 766 if(s) 767 return apr_brigade_printf(bb, NULL, NULL, ",\n \"%s\": \"%s\"", name, s); 768 return 0; 769 } 770 771 static int curltest_sslinfo_handler(request_rec *r) 772 { 773 conn_rec *c = r->connection; 774 apr_bucket_brigade *bb; 775 apr_bucket *b; 776 apr_status_t rv; 777 apr_array_header_t *args = NULL; 778 const char *request_id = NULL; 779 int close_conn = 0; 780 long l; 781 int i; 782 783 if(strcmp(r->handler, "curltest-sslinfo")) { 784 return DECLINED; 785 } 786 if(r->method_number != M_GET) { 787 return DECLINED; 788 } 789 790 if(r->args) { 791 apr_array_header_t *args = apr_cstr_split(r->args, "&", 1, r->pool); 792 for(i = 0; i < args->nelts; ++i) { 793 char *s, *val, *arg = APR_ARRAY_IDX(args, i, char *); 794 s = strchr(arg, '='); 795 if(s) { 796 *s = '\0'; 797 val = s + 1; 798 if(!strcmp("id", arg)) { 799 /* just an id for repeated requests with curl's url globbing */ 800 request_id = val; 801 continue; 802 } 803 } 804 else if(!strcmp("close", arg)) { 805 /* we are asked to close the connection */ 806 close_conn = 1; 807 continue; 808 } 809 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not " 810 "understood: '%s' in %s", 811 arg, r->args); 812 ap_die(HTTP_BAD_REQUEST, r); 813 return OK; 814 } 815 } 816 817 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "sslinfo: processing"); 818 r->status = 200; 819 r->clength = -1; 820 r->chunked = 1; 821 apr_table_unset(r->headers_out, "Content-Length"); 822 /* Discourage content-encodings */ 823 apr_table_unset(r->headers_out, "Content-Encoding"); 824 apr_table_setn(r->subprocess_env, "no-brotli", "1"); 825 apr_table_setn(r->subprocess_env, "no-gzip", "1"); 826 827 ap_set_content_type(r, "application/json"); 828 829 bb = apr_brigade_create(r->pool, c->bucket_alloc); 830 831 apr_brigade_puts(bb, NULL, NULL, "{\n \"Name\": \"SSL-Information\""); 832 brigade_env_var(r, bb, "HTTPS"); 833 brigade_env_var(r, bb, "SSL_PROTOCOL"); 834 brigade_env_var(r, bb, "SSL_CIPHER"); 835 brigade_env_var(r, bb, "SSL_SESSION_ID"); 836 brigade_env_var(r, bb, "SSL_SESSION_RESUMED"); 837 brigade_env_var(r, bb, "SSL_SRP_USER"); 838 brigade_env_var(r, bb, "SSL_SRP_USERINFO"); 839 brigade_env_var(r, bb, "SSL_TLS_SNI"); 840 apr_brigade_puts(bb, NULL, NULL, "}\n"); 841 842 /* flush response */ 843 b = apr_bucket_flush_create(c->bucket_alloc); 844 APR_BRIGADE_INSERT_TAIL(bb, b); 845 rv = ap_pass_brigade(r->output_filters, bb); 846 if(APR_SUCCESS != rv) 847 goto cleanup; 848 849 /* we are done */ 850 b = apr_bucket_eos_create(c->bucket_alloc); 851 APR_BRIGADE_INSERT_TAIL(bb, b); 852 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "1_1_handler: request read"); 853 854 rv = ap_pass_brigade(r->output_filters, bb); 855 856 cleanup: 857 if(close_conn) 858 r->connection->keepalive = AP_CONN_CLOSE; 859 if(rv == APR_SUCCESS 860 || r->status != HTTP_OK 861 || c->aborted) { 862 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler: done"); 863 return OK; 864 } 865 else { 866 /* no way to know what type of error occurred */ 867 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler failed"); 868 return AP_FILTER_ERROR; 869 } 870 return DECLINED; 871 }