diff --git a/driver-http/src/main/java/io/nosqlbench/activitytype/cmds/HttpFormatParser.java b/driver-http/src/main/java/io/nosqlbench/activitytype/cmds/HttpFormatParser.java index 783dada09..ad190d94c 100644 --- a/driver-http/src/main/java/io/nosqlbench/activitytype/cmds/HttpFormatParser.java +++ b/driver-http/src/main/java/io/nosqlbench/activitytype/cmds/HttpFormatParser.java @@ -8,12 +8,13 @@ import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; +import java.util.regex.Pattern; public class HttpFormatParser { public static Map parseUrl(String uri) { if (uri.matches("http.+")) { - return Map.of("uri", rewriteUriWithStaticsEncoded(uri)); + return Map.of("uri", rewriteExplicitSections(uri)); } return null; } @@ -54,14 +55,14 @@ public class HttpFormatParser { throw new BasicError("Request template must have at least a method and a uri: " + methodAndHeaders[0]); } props.put("method", methodLine[0]); - props.put("uri", rewriteUriWithStaticsEncoded(methodLine[1])); + props.put("uri", rewriteExplicitSections(methodLine[1])); if (methodLine.length == 3) { String actualVersion = methodLine[2]; String symbolicVersion = actualVersion .replaceAll("/1.1", "_1_1") .replaceAll("/2.0", "_2") - .replaceAll("/2", "_2"); + .replaceAll("/2", "_2"); props.put("version", symbolicVersion); } @@ -69,27 +70,37 @@ public class HttpFormatParser { return props; } - private static String rewriteUriWithStaticsEncoded(String template) { - String[] parts = template.split("\\?", 2); + private final static Pattern DOENCODE = Pattern.compile("(URLENCODE|E)\\[\\[(?.+?)\\]\\]"); - if (parts.length == 2) { - StringBuilder sb = new StringBuilder(); - String input = parts[1]; - Matcher matcher = ParsedTemplate.STANDARD_ANCHOR.matcher(input); - int idx = 0; - while (matcher.find()) { - String pre = input.substring(0, matcher.start()); - sb.append(URLEncoder.encode(pre, StandardCharsets.UTF_8)); - sb.append(matcher.group()); - idx = matcher.end(); -// matcher.appendReplacement(sb, "test-value" + idx); - } - sb.append(URLEncoder.encode(input.substring(idx), StandardCharsets.UTF_8)); - return parts[0] + "?" + sb.toString(); - } else { - return template; + private static String rewriteExplicitSections(String template) { + + StringBuilder sb = new StringBuilder(); + Matcher matcher = DOENCODE.matcher(template); + while (matcher.find()) { + String rewrite = matcher.group("data"); + String encoded = rewriteStaticsOnly(rewrite); + matcher.appendReplacement(sb, encoded); } + matcher.appendTail(sb); + return sb.toString(); } + private static String rewriteStaticsOnly(String template) { + + StringBuilder sb = new StringBuilder(); + String input = template; + Matcher matcher = ParsedTemplate.STANDARD_ANCHOR.matcher(input); + int idx = 0; + while (matcher.find()) { + String pre = input.substring(0, matcher.start()); + sb.append(URLEncoder.encode(pre, StandardCharsets.UTF_8)); + sb.append(matcher.group()); + idx = matcher.end(); +// matcher.appendReplacement(sb, "test-value" + idx); + } + sb.append(URLEncoder.encode(input.substring(idx), StandardCharsets.UTF_8)); + return sb.toString(); + } + } diff --git a/driver-http/src/main/resources/http.md b/driver-http/src/main/resources/http.md index 3b09b31f4..392039193 100644 --- a/driver-http/src/main/resources/http.md +++ b/driver-http/src/main/resources/http.md @@ -45,7 +45,7 @@ You can even make a detailed request with custom headers and result verification ```yaml # Require that the result be status code 200-299 match regex "OK, account id is .*" in the body statements: - - name: 'get-from-google' + - name: 'get-from-google' method: GET uri: "https://google.com/" version: "HTTP/1.1" @@ -120,12 +120,19 @@ All other request fields are optional and have reasonable defaults: - **uri** - This is the URI that you might put into the URL bar of your browser. There is no default. Example: `https://en.wikipedia.org/wiki/Leonhard_Euler` + If the uri contains a question mark '?' as a query delimiter, then all - characters after this are automatically URL encoded. This is done for - any literal part of the uri. If you use bindings in the uri as - in `https://en.wikipedia.org/wiki/{topic}`, then it is up to you to - ensure that the values are produced in a valid form for a URI. You can - use the `URLEncode()` binding function where needed to achieve this. + embedded sections which are contained within `URLENCODE[[` ... `]]` + sections are preprocessed by the HTTP driver. This allows you to keep + your test data in a recognizable form. This is done at startup, so there + is no cost during the test run. As an added convenience, binding points + which are within the encoded block will be preserved, so + both `https://en.wikipedia.org/URLENCODE[[wiki/]]{topic}` and + `https://en.wikipedia.org/URLENCODE[[wiki/{topic}]]` will yield the same + configuration. For a terser form, you can use `E[[...]]`. You must also + ensure that the values that are inserted at binding points are produced + in a valid form for a URI. You can use the `URLEncode()` + binding function where needed to achieve this. - **method** - An optional request method. If not provided, "GET" is assumed. Any method name will work here, even custom ones that are specific to a given target system. No validation is done for standard method names, as there is no way to know what method names may be valid. @@ -177,49 +184,49 @@ results. Support may be added for long-lived connections in a future release. - **follow_redirects** - default: normal - One of never, always, or normal. Normal redirects are those which do not redirect from HTTPS to HTTP. - + - **diagnostics** - default: none - synonym: **diag** - example: `diag=brief,1000` - print diagnostics for every 1000th - cycle, including only brief details as explained below. - - This setting is a selector for what level of verbosity you will get - on the console. If you set this to true, you'll get every request - and response logged to console. This is only for verifying that a test - is configured and to spot check services before running higher scale - tests. - - All of the data shown in diagnostics is post-hoc, directly from - the response provided by the internal HTTP client in the Java runtime. - + example: `diag=brief,1000` - print diagnostics for every 1000th cycle, + including only brief details as explained below. + + This setting is a selector for what level of verbosity you will get on + the console. If you set this to true, you'll get every request and + response logged to console. This is only for verifying that a test is + configured and to spot check services before running higher scale tests. + + All of the data shown in diagnostics is post-hoc, directly from the + response provided by the internal HTTP client in the Java runtime. + If you want finer control over how much information diagnostics provides, you can specify a comma separated list of the below. - - - headers - show headers - - stats - show basic stats of each request - - data10 - show only the first 10 characters of each response body - - data100 - show only the first 100 characters of each response body - this setting supersedes `data10` - - data1000 - show only the first 1000 characters of each response body - this setting supersedes `data100` - - data - show all of each response body - this setting supersedes `data1000` - - redirects - show details for interstitial request which are made - when the client follows a redirect directive like a `location` header. - - requests - show details for requests - - responses - show details for responses - - brief - Show headers, stats, requests, responses, and 10 characters - - all - Show everything, including full payloads and redirects - - a modulo - any number, like 3000 - causes the diagnostics to be - reported only on this cycle modulo. If you set `diag=300,brief` - then you will get the brief diagnostic output for every 300th - response. + + - headers - show headers + - stats - show basic stats of each request + - data10 - show only the first 10 characters of each response body + - data100 - show only the first 100 characters of each response body + this setting supersedes `data10` + - data1000 - show only the first 1000 characters of each response body + this setting supersedes `data100` + - data - show all of each response body this setting + supersedes `data1000` + - redirects - show details for interstitial request which are made + when the client follows a redirect directive like a `location` + header. + - requests - show details for requests + - responses - show details for responses + - brief - Show headers, stats, requests, responses, and 10 characters + - all - Show everything, including full payloads and redirects + - a modulo - any number, like 3000 - causes the diagnostics to be + reported only on this cycle modulo. If you set `diag=300,brief` + then you will get the brief diagnostic output for every 300th + response. The requests, responses, and redirects setting work intersectionally. For example, if you specify responses, and redirect, but not requests, then you will only see the response portion of all calls made by the client. - - All of the diagnostic filters are incrementally added. - -- **timeout** - default: forever - - Sets the timeout of each request in milliseconds. + + All of the diagnostic filters are incrementally added. + +- **timeout** - default: forever - Sets the timeout of each request in + milliseconds.