quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

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 }