@@ -68,6 +68,12 @@ struct Metrics {
6868 unsigned long long parse_error_count{0 };
6969};
7070
71+ class JsonBenchmarkError : public std ::runtime_error {
72+ public:
73+ explicit JsonBenchmarkError (const std::string &message)
74+ : std::runtime_error(message) { }
75+ };
76+
7177const char *const usage_message =
7278 " Usage: json_benchmark --scenario NAME [--iterations N] "
7379 " [--target-bytes N] [--depth N] [--include-invalid] [--output json]" ;
@@ -99,7 +105,7 @@ unsigned long long parseUnsignedLongLong(const char *value,
99105 const unsigned long long parsed = std::strtoull (value, &end, 10 );
100106 if (errno != 0 || end == value || *end != ' \0 '
101107 || (!allow_zero && parsed == 0 )) {
102- throw std::runtime_error (std::string (" invalid numeric value for " )
108+ throw JsonBenchmarkError (std::string (" invalid numeric value for " )
103109 + flag_name + " : " + value);
104110 }
105111 return parsed;
@@ -109,7 +115,7 @@ std::size_t parseSize(const char *value, const char *flag_name) {
109115 const unsigned long long parsed =
110116 parseUnsignedLongLong (value, flag_name, true );
111117 if (parsed > std::numeric_limits<std::size_t >::max ()) {
112- throw std::runtime_error (std::string (" value too large for " )
118+ throw JsonBenchmarkError (std::string (" value too large for " )
113119 + flag_name + " : " + value);
114120 }
115121 return static_cast <std::size_t >(parsed);
@@ -119,59 +125,62 @@ unsigned long long parseIterations(const char *value) {
119125 return parseUnsignedLongLong (value, " --iterations" , false );
120126}
121127
128+ const char *requireOptionValue (int argc, const char *argv[], int *index,
129+ const char *option_name) {
130+ if (*index + 1 >= argc) {
131+ throw JsonBenchmarkError (std::string (" missing value for " )
132+ + option_name);
133+ }
134+ *index += 1 ;
135+ return argv[*index];
136+ }
137+
122138Options parseOptions (int argc, const char *argv[]) {
123139 Options options;
124140
125- for (int i = 1 ; i < argc; i++) {
141+ int i = 1 ;
142+ while (i < argc) {
126143 const std::string current (argv[i]);
127144 if (current == " -h" || current == " -?" || current == " --help" ) {
128145 std::cout << usage_message << std::endl;
129146 std::exit (0 );
130147 } else if (current == " --scenario" ) {
131- if (i + 1 >= argc) {
132- throw std::runtime_error (" missing value for --scenario" );
133- }
134- options.scenario .assign (argv[++i]);
148+ options.scenario .assign (
149+ requireOptionValue (argc, argv, &i, " --scenario" ));
135150 } else if (current == " --iterations" ) {
136- if (i + 1 >= argc) {
137- throw std::runtime_error (" missing value for --iterations" );
138- }
139- options.iterations = parseIterations (argv[++i]);
151+ options.iterations = parseIterations (
152+ requireOptionValue (argc, argv, &i, " --iterations" ));
140153 } else if (current == " --target-bytes" ) {
141- if (i + 1 >= argc) {
142- throw std::runtime_error (" missing value for --target-bytes" );
143- }
144- options.target_bytes = parseSize (argv[++i], " --target-bytes" );
154+ options.target_bytes = parseSize (
155+ requireOptionValue (argc, argv, &i, " --target-bytes" ),
156+ " --target-bytes" );
145157 } else if (current == " --depth" ) {
146- if (i + 1 >= argc) {
147- throw std::runtime_error (" missing value for --depth" );
148- }
149- options.depth = parseSize (argv[++i], " --depth" );
158+ options.depth = parseSize (
159+ requireOptionValue (argc, argv, &i, " --depth" ), " --depth" );
150160 } else if (current == " --include-invalid" ) {
151161 options.include_invalid = true ;
152162 } else if (current == " --output" ) {
153- if (i + 1 >= argc) {
154- throw std::runtime_error (" missing value for --output" );
155- }
156- if (const std::string output_format (argv[++i]);
163+ if (const std::string output_format (
164+ requireOptionValue (argc, argv, &i, " --output" ));
157165 output_format != " json" ) {
158- throw std::runtime_error (" unsupported output format: "
166+ throw JsonBenchmarkError (" unsupported output format: "
159167 + output_format);
160168 }
161169 options.output_json = true ;
162170 } else {
163- throw std::runtime_error (" unknown option: " + current);
171+ throw JsonBenchmarkError (" unknown option: " + current);
164172 }
173+ i++;
165174 }
166175
167176 if (options.scenario .empty ()) {
168- throw std::runtime_error (" missing required --scenario" );
177+ throw JsonBenchmarkError (" missing required --scenario" );
169178 }
170179
171180 if (const bool is_invalid_scenario = options.scenario == " truncated"
172181 || options.scenario == " malformed" ;
173182 is_invalid_scenario && !options.include_invalid ) {
174- throw std::runtime_error (
183+ throw JsonBenchmarkError (
175184 " invalid JSON scenarios require --include-invalid" );
176185 }
177186
@@ -301,7 +310,7 @@ std::string buildScenarioBody(const Options &options) {
301310 return body;
302311 }
303312
304- throw std::runtime_error (" unsupported scenario: " + options.scenario );
313+ throw JsonBenchmarkError (" unsupported scenario: " + options.scenario );
305314}
306315
307316bool isResolvedZero (const std::unique_ptr<std::string> &value) {
@@ -332,13 +341,13 @@ Metrics runBenchmark(modsecurity::ModSecurity *modsec,
332341 reinterpret_cast <const unsigned char *>(body.data ()), body.size ());
333342 metrics.append_request_body_ns += elapsedNanos (append_start);
334343 if (append_ok == 0 ) {
335- throw std::runtime_error (
344+ throw JsonBenchmarkError (
336345 " appendRequestBody reported partial body processing" );
337346 }
338347
339348 const auto process_start = Clock::now ();
340349 if (!transaction.processRequestBody ()) {
341- throw std::runtime_error (" processRequestBody returned false" );
350+ throw JsonBenchmarkError (" processRequestBody returned false" );
342351 }
343352 metrics.process_request_body_ns += elapsedNanos (process_start);
344353 metrics.total_transaction_ns += elapsedNanos (total_start);
@@ -349,7 +358,7 @@ Metrics runBenchmark(modsecurity::ModSecurity *modsec,
349358 transaction.m_variableReqbodyProcessorError .resolveFirst ();
350359
351360 if (!reqbody_error || !processor_error) {
352- throw std::runtime_error (
361+ throw JsonBenchmarkError (
353362 " unable to resolve JSON parse outcome variables" );
354363 }
355364
@@ -358,7 +367,7 @@ Metrics runBenchmark(modsecurity::ModSecurity *modsec,
358367 if (const bool parse_error = !isResolvedZero (reqbody_error)
359368 || !isResolvedZero (processor_error);
360369 parse_success == parse_error) {
361- throw std::runtime_error (
370+ throw JsonBenchmarkError (
362371 " ambiguous JSON parse outcome observed in benchmark" );
363372 }
364373
0 commit comments