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