mirror of
https://github.com/nosqlbench/nosqlbench.git
synced 2025-02-25 18:55:28 -06:00
rebuild http driver on new API
This commit is contained in:
parent
586bb4c566
commit
8d87c6aee1
@ -24,7 +24,7 @@
|
||||
<relativePath>../mvn-defaults</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>driver-http</artifactId>
|
||||
<artifactId>adapter-http</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>${project.artifactId}</name>
|
||||
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.adapter.http;
|
||||
|
||||
import io.nosqlbench.adapter.http.core.HttpFormatParser;
|
||||
import io.nosqlbench.adapter.http.core.HttpOp;
|
||||
import io.nosqlbench.adapter.http.core.HttpOpMapper;
|
||||
import io.nosqlbench.adapter.http.core.HttpSpace;
|
||||
import io.nosqlbench.engine.api.activityimpl.OpMapper;
|
||||
import io.nosqlbench.engine.api.activityimpl.uniform.BaseDriverAdapter;
|
||||
import io.nosqlbench.engine.api.activityimpl.uniform.DriverAdapter;
|
||||
import io.nosqlbench.engine.api.activityimpl.uniform.DriverSpaceCache;
|
||||
import io.nosqlbench.nb.annotations.Service;
|
||||
import io.nosqlbench.nb.api.config.standard.NBConfigModel;
|
||||
import io.nosqlbench.nb.api.config.standard.NBConfiguration;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Service(value = DriverAdapter.class, selector = "http")
|
||||
public class HttpDriverAdapter extends BaseDriverAdapter<HttpOp, HttpSpace> {
|
||||
|
||||
@Override
|
||||
public OpMapper<HttpOp> getOpMapper() {
|
||||
DriverSpaceCache<? extends HttpSpace> spaceCache = getSpaceCache();
|
||||
NBConfiguration config = getConfiguration();
|
||||
return new HttpOpMapper(config, spaceCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<String, ? extends HttpSpace> getSpaceInitializer(NBConfiguration cfg) {
|
||||
return spaceName -> new HttpSpace(spaceName, cfg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Function<Map<String, Object>, Map<String, Object>>> getOpFieldRemappers() {
|
||||
return super.getOpFieldRemappers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Function<String, Optional<Map<String, Object>>>> getOpStmtRemappers() {
|
||||
return List.of(
|
||||
s -> Optional.ofNullable(HttpFormatParser.parseUrl(s))
|
||||
.map(LinkedHashMap::new),
|
||||
s -> Optional.ofNullable(HttpFormatParser.parseInline(s))
|
||||
.map(LinkedHashMap::new),
|
||||
s -> Optional.ofNullable(HttpFormatParser.parseParams(s))
|
||||
.map(LinkedHashMap::new)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBConfigModel getConfigModel() {
|
||||
return super.getConfigModel().add(HttpSpace.getConfigModel());
|
||||
}
|
||||
}
|
@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.http;
|
||||
package io.nosqlbench.adapter.http.core;
|
||||
|
||||
import io.nosqlbench.activitytype.http.statuscodes.HttpStatusCodes;
|
||||
import io.nosqlbench.adapter.http.statuscodes.HttpStatusCodes;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.net.http.HttpClient;
|
@ -14,8 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.cmds;
|
||||
package io.nosqlbench.adapter.http.core;
|
||||
|
||||
import io.nosqlbench.nb.api.config.params.ParamsParser;
|
||||
import io.nosqlbench.nb.api.errors.BasicError;
|
||||
import io.nosqlbench.virtdata.core.templates.BindPointParser;
|
||||
|
||||
@ -28,6 +29,13 @@ import java.util.regex.Pattern;
|
||||
|
||||
public class HttpFormatParser {
|
||||
|
||||
public static Map<String,String> parseParams(String oneliner) {
|
||||
if (ParamsParser.hasValues(oneliner)) {
|
||||
return ParamsParser.parse(oneliner,false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Map<String, String> parseUrl(String uri) {
|
||||
if (uri.matches("http.+")) {
|
||||
return Map.of("uri", rewriteExplicitSections(uri));
|
@ -14,20 +14,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.http;
|
||||
package io.nosqlbench.adapter.http.core;
|
||||
|
||||
import io.nosqlbench.engine.api.activityapi.core.Action;
|
||||
import io.nosqlbench.engine.api.activityapi.core.ActionDispenser;
|
||||
import com.codahale.metrics.Histogram;
|
||||
import io.nosqlbench.api.NBNamedElement;
|
||||
import io.nosqlbench.engine.api.metrics.ActivityMetrics;
|
||||
|
||||
public class HttpActionDispenser implements ActionDispenser {
|
||||
private final HttpActivity httpActivity;
|
||||
public class HttpMetrics implements NBNamedElement {
|
||||
private final HttpSpace space;
|
||||
final Histogram statusCodeHistogram;
|
||||
|
||||
public HttpActionDispenser(HttpActivity httpActivity) {
|
||||
this.httpActivity = httpActivity;
|
||||
public HttpMetrics(HttpSpace space) {
|
||||
this.space = space;
|
||||
statusCodeHistogram = ActivityMetrics.histogram(this, "statuscode",space.getHdrDigits());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action getAction(int i) {
|
||||
return new HttpAction(httpActivity.getActivityDef(), i, httpActivity);
|
||||
public String getName() {
|
||||
return "http"+(space.getName().equals("default")?"":"-"+space.getName());
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.adapter.http.core;
|
||||
|
||||
import io.nosqlbench.adapter.http.errors.InvalidResponseBodyException;
|
||||
import io.nosqlbench.adapter.http.errors.InvalidStatusCodeException;
|
||||
import io.nosqlbench.engine.api.activityimpl.uniform.flowtypes.RunnableOp;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class HttpOp implements RunnableOp {
|
||||
|
||||
public final Pattern ok_status;
|
||||
public final Pattern ok_body;
|
||||
public final HttpRequest request;
|
||||
private final HttpClient client;
|
||||
private final HttpSpace space;
|
||||
private final long cycle;
|
||||
|
||||
public HttpOp(HttpClient client, HttpRequest request, Pattern ok_status, Pattern ok_body, HttpSpace space, long cycle) {
|
||||
this.client = client;
|
||||
this.request = request;
|
||||
this.ok_status = ok_status;
|
||||
this.ok_body = ok_body;
|
||||
this.space = space;
|
||||
this.cycle = cycle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
HttpResponse.BodyHandler<String> bodyreader = HttpResponse.BodyHandlers.ofString();
|
||||
HttpResponse<String> response = null;
|
||||
Exception error = null;
|
||||
long startat = System.nanoTime();
|
||||
try {
|
||||
CompletableFuture<HttpResponse<String>> responseFuture = client.sendAsync(request, bodyreader);
|
||||
response = responseFuture.get(space.getTimeoutMillis(), TimeUnit.MILLISECONDS);
|
||||
space.getHttpMetrics().statusCodeHistogram.update(response.statusCode());
|
||||
|
||||
if (ok_status != null) {
|
||||
if (!ok_status.matcher(String.valueOf(response.statusCode())).matches()) {
|
||||
throw new InvalidStatusCodeException(ok_status, response.statusCode());
|
||||
}
|
||||
}
|
||||
if (ok_body != null) {
|
||||
if (!ok_body.matcher(response.body()).matches()) {
|
||||
throw new InvalidResponseBodyException(ok_body, response.body());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
error = e;
|
||||
} finally {
|
||||
long nanos = System.nanoTime() - startat;
|
||||
if (space.isDiagnosticMode()) {
|
||||
space.getConsole().summarizeRequest("request", error, request, System.out, cycle, nanos);
|
||||
if (response != null) {
|
||||
space.getConsole().summarizeResponseChain(error, response, System.out, cycle, nanos);
|
||||
} else {
|
||||
System.out.println("---- RESPONSE was null");
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
// propogate exception so main error handling logic can take over
|
||||
if (error!=null) {
|
||||
throw new RuntimeException(error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.adapter.http.core;
|
||||
|
||||
import io.nosqlbench.engine.api.activityimpl.BaseOpDispenser;
|
||||
import io.nosqlbench.engine.api.templating.ParsedOp;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.LongFunction;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class HttpOpDispenser extends BaseOpDispenser<HttpOp> {
|
||||
|
||||
private final LongFunction<HttpOp> opFunc;
|
||||
public static final String DEFAULT_OK_BODY = ".+?";
|
||||
public static final String DEFAULT_OK_STATUS = "2..";
|
||||
|
||||
|
||||
public HttpOpDispenser(LongFunction<HttpSpace> ctxF, ParsedOp op) {
|
||||
super(op);
|
||||
opFunc = getOpFunc(ctxF, op);
|
||||
}
|
||||
|
||||
private LongFunction<HttpOp> getOpFunc(LongFunction<HttpSpace> ctxF, ParsedOp op) {
|
||||
|
||||
LongFunction<HttpRequest.Builder> builderF = l -> HttpRequest.newBuilder();
|
||||
LongFunction<String> bodyF = op.getAsFunctionOr("body", null);
|
||||
LongFunction<HttpRequest.BodyPublisher> bodyPublisherF =
|
||||
l -> Optional.ofNullable(bodyF.apply(l)).map(HttpRequest.BodyPublishers::ofString).orElse(
|
||||
HttpRequest.BodyPublishers.noBody()
|
||||
);
|
||||
|
||||
LongFunction<String> methodF = op.getAsFunctionOr("method", "GET");
|
||||
LongFunction<HttpRequest.Builder> initBuilderF =
|
||||
l -> builderF.apply(l).method(methodF.apply(l), bodyPublisherF.apply(l));
|
||||
|
||||
initBuilderF = op.enhanceFuncOptionally(
|
||||
initBuilderF, "version", String.class,
|
||||
(b, v) -> b.version(HttpClient.Version.valueOf(
|
||||
v.replaceAll("/1.1", "_1_1")
|
||||
.replaceAll("/2.0", "_2")
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
initBuilderF = op.enhanceFuncOptionally(initBuilderF, "uri", String.class, (b, v) -> b.uri(URI.create(v)));
|
||||
|
||||
op.getOptionalStaticValue("follow_redirects",boolean.class);
|
||||
|
||||
/**
|
||||
* Add header adders for any key provided in the op template which is capitalized
|
||||
*/
|
||||
List<String> headerNames = op.getDefinedNames().stream()
|
||||
.filter(n -> n.charAt(0) >= 'A')
|
||||
.filter(n -> n.charAt(0) <= 'Z')
|
||||
.toList();
|
||||
if (headerNames.size()>0) {
|
||||
for (String headerName : headerNames) {
|
||||
initBuilderF = op.enhanceFunc(initBuilderF,headerName,String.class, (b,h) -> b.header(headerName,h));
|
||||
}
|
||||
}
|
||||
|
||||
initBuilderF = op.enhanceFuncOptionally(initBuilderF,"timeout",long.class,(b,v) -> b.timeout(Duration.ofMillis(v)));
|
||||
|
||||
LongFunction<HttpRequest.Builder> finalInitBuilderF = initBuilderF;
|
||||
LongFunction<HttpRequest> reqF = l -> finalInitBuilderF.apply(l).build();
|
||||
|
||||
|
||||
Pattern ok_status = op.getOptionalStaticValue("ok-status",String.class)
|
||||
.map(Pattern::compile)
|
||||
.orElse(Pattern.compile(DEFAULT_OK_STATUS));
|
||||
|
||||
Pattern ok_body = op.getOptionalStaticValue("ok-body", String.class)
|
||||
.map(Pattern::compile)
|
||||
.orElse(null);
|
||||
|
||||
LongFunction<HttpOp> opFunc = cycle -> new HttpOp(
|
||||
ctxF.apply(cycle).getClient(),
|
||||
reqF.apply(cycle),
|
||||
ok_status,
|
||||
ok_body,
|
||||
ctxF.apply(cycle),cycle
|
||||
);
|
||||
return opFunc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpOp apply(long value) {
|
||||
HttpOp op = this.opFunc.apply(value);
|
||||
return op;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.adapter.http.core;
|
||||
|
||||
import io.nosqlbench.engine.api.activityimpl.OpDispenser;
|
||||
import io.nosqlbench.engine.api.activityimpl.OpMapper;
|
||||
import io.nosqlbench.engine.api.activityimpl.uniform.DriverSpaceCache;
|
||||
import io.nosqlbench.engine.api.templating.ParsedOp;
|
||||
import io.nosqlbench.nb.api.config.standard.NBConfiguration;
|
||||
|
||||
import java.util.function.LongFunction;
|
||||
|
||||
public class HttpOpMapper implements OpMapper<HttpOp> {
|
||||
|
||||
private final NBConfiguration cfg;
|
||||
private final DriverSpaceCache<? extends HttpSpace> spaceCache;
|
||||
|
||||
public HttpOpMapper(NBConfiguration cfg, DriverSpaceCache<? extends HttpSpace> spaceCache) {
|
||||
this.cfg = cfg;
|
||||
this.spaceCache = spaceCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpDispenser<? extends HttpOp> apply(ParsedOp cmd) {
|
||||
LongFunction<String> spaceNameF = cmd.getAsFunctionOr("space", "default");
|
||||
LongFunction<HttpSpace> spaceFunc = l -> spaceCache.get(spaceNameF.apply(l));
|
||||
return new HttpOpDispenser(spaceFunc,cmd);
|
||||
}
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.adapter.http.core;
|
||||
|
||||
import io.nosqlbench.api.NBNamedElement;
|
||||
import io.nosqlbench.nb.api.config.standard.ConfigModel;
|
||||
import io.nosqlbench.nb.api.config.standard.NBConfigModel;
|
||||
import io.nosqlbench.nb.api.config.standard.NBConfiguration;
|
||||
import io.nosqlbench.nb.api.config.standard.Param;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* ThreadLocal http clients have been removed from this version, as the built-in
|
||||
* HTTP client implementation is meant to be immutable. If shared-state issues
|
||||
* occur, thread-local support will be re-added.
|
||||
*/
|
||||
public class HttpSpace implements NBNamedElement {
|
||||
private final static Logger logger = LogManager.getLogger(HttpSpace.class);
|
||||
|
||||
private final String name;
|
||||
private final NBConfiguration cfg;
|
||||
private HttpConsoleFormats console;
|
||||
private HttpClient.Redirect followRedirects;
|
||||
private Duration timeout;
|
||||
private long timeoutMillis;
|
||||
private final HttpClient httpclient;
|
||||
private int hdrDigits;
|
||||
private HttpMetrics httpMetrics;
|
||||
private boolean diagnosticsEnabled;
|
||||
|
||||
|
||||
public HttpSpace(String spaceName, NBConfiguration cfg) {
|
||||
this.name = spaceName;
|
||||
this.cfg = cfg;
|
||||
applyConfig(cfg);
|
||||
this.httpclient = newClient();
|
||||
}
|
||||
|
||||
public HttpClient getClient() {
|
||||
return this.httpclient;
|
||||
}
|
||||
|
||||
private HttpClient newClient() {
|
||||
HttpClient.Builder builder = HttpClient.newBuilder();
|
||||
logger.debug("follow_redirects=>" + followRedirects);
|
||||
builder = builder.followRedirects(this.followRedirects);
|
||||
builder = builder.connectTimeout(this.timeout);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public synchronized void applyConfig(NBConfiguration cfg) {
|
||||
this.followRedirects =
|
||||
HttpClient.Redirect.valueOf(
|
||||
cfg.get("follow_redirects").toUpperCase(Locale.ROOT)
|
||||
);
|
||||
this.timeout = Duration.ofMillis(cfg.get("timeout", long.class));
|
||||
this.timeoutMillis = cfg.get("timeout", long.class);
|
||||
this.httpMetrics = new HttpMetrics(this);
|
||||
|
||||
this.console = cfg.getOptional("diag").map(s -> HttpConsoleFormats.apply(s, this.console))
|
||||
.orElseGet(() -> HttpConsoleFormats.apply(null,null));
|
||||
|
||||
this.diagnosticsEnabled = console.isDiagnosticMode();
|
||||
|
||||
}
|
||||
|
||||
public long getTimeoutMillis() {
|
||||
return timeoutMillis;
|
||||
}
|
||||
|
||||
public int getHdrDigits() {
|
||||
return hdrDigits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public HttpMetrics getHttpMetrics() {
|
||||
return httpMetrics;
|
||||
}
|
||||
|
||||
public boolean isDiagnosticMode() {
|
||||
return diagnosticsEnabled;
|
||||
}
|
||||
|
||||
public HttpConsoleFormats getConsole() {
|
||||
return console;
|
||||
}
|
||||
|
||||
public static NBConfigModel getConfigModel() {
|
||||
return ConfigModel.of(HttpSpace.class)
|
||||
.add(Param.defaultTo("follow_redirects", "normal")
|
||||
.setRegex("normal|always|never")
|
||||
.setDescription("Whether to follow redirects. Normal redirects are those which do not " +
|
||||
"redirect from HTTPS to HTTP.")
|
||||
)
|
||||
.add(Param.optional(List.of("diag","diagnostics"), String.class)
|
||||
.setDescription("Print extended diagnostics. This option has numerous" +
|
||||
" possible values. See the markdown docs for details. (nb help http)")
|
||||
)
|
||||
.add(Param.defaultTo("timeout", Long.MAX_VALUE)
|
||||
.setDescription("How long to wait for requests before timeout out. Default is forever."))
|
||||
.add(Param.defaultTo("hdr_digits", 4)
|
||||
.setDescription("number of digits of precision to keep in HDR histograms"))
|
||||
.asReadOnly();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -14,18 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.http;
|
||||
package io.nosqlbench.adapter.http.errors;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class InvalidResponseBodyException extends RuntimeException {
|
||||
private final long cycleValue;
|
||||
private final Pattern ok_body;
|
||||
private final String body;
|
||||
|
||||
public InvalidResponseBodyException(long cycleValue, Pattern ok_body, String body) {
|
||||
this.cycleValue = cycleValue;
|
||||
public InvalidResponseBodyException(Pattern ok_body, String body) {
|
||||
this.ok_body = ok_body;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "Server returned body which failed content check with pattern '" + ok_body.pattern() + "'";
|
||||
}
|
||||
}
|
@ -14,12 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.http;
|
||||
package io.nosqlbench.adapter.http.errors;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class InvalidStatusCodeException extends RuntimeException {
|
||||
private final long cycleValue;
|
||||
private final Pattern ok_status;
|
||||
private final int statusCode;
|
||||
|
||||
@ -28,8 +27,7 @@ public class InvalidStatusCodeException extends RuntimeException {
|
||||
return "Server returned status code '" + statusCode + "' which did not match ok_status '" + ok_status.toString() + "'";
|
||||
}
|
||||
|
||||
public InvalidStatusCodeException(long cycleValue, Pattern ok_status, int statusCode) {
|
||||
this.cycleValue = cycleValue;
|
||||
public InvalidStatusCodeException(Pattern ok_status, int statusCode) {
|
||||
this.ok_status = ok_status;
|
||||
this.statusCode = statusCode;
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.http.statuscodes;
|
||||
package io.nosqlbench.adapter.http.statuscodes;
|
||||
|
||||
import io.nosqlbench.nb.api.content.Content;
|
||||
import io.nosqlbench.nb.api.content.NBIO;
|
@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.http.statuscodes;
|
||||
package io.nosqlbench.adapter.http.statuscodes;
|
||||
|
||||
enum HttpStatusRanges {
|
||||
public enum HttpStatusRanges {
|
||||
Informational("INFORMATIONAL", 100, 199, "Request received, continuing process"),
|
||||
Success("SUCCESS",200, 299, "Request successfully received, understood, and accepted"),
|
||||
Redirection("REDIRECTION", 300, 399, "Further action must be taken in order to complete the request."),
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.http.statuscodes;
|
||||
package io.nosqlbench.adapter.http.statuscodes;
|
||||
|
||||
public class IetfStatusCode {
|
||||
private final String values;
|
@ -15,14 +15,14 @@ parameters and executed.
|
||||
The simplest possible statement form looks like this:
|
||||
|
||||
```yaml
|
||||
statement: http://google.com/
|
||||
op: http://google.com/
|
||||
```
|
||||
|
||||
Or, you can have a list:
|
||||
|
||||
```yaml
|
||||
# A list of statements
|
||||
statements:
|
||||
ops:
|
||||
- http://google.com/
|
||||
- http://amazon.com/
|
||||
```
|
||||
@ -31,7 +31,7 @@ Or you can template the values used in the URI, and even add ratios:
|
||||
|
||||
```yaml
|
||||
# A list of named statements with variable fields and specific ratios:
|
||||
statements:
|
||||
ops:
|
||||
- s1: http://google.com/search?query={query}
|
||||
ratio: 3
|
||||
- s2: https://www.amazon.com/s?k={query}
|
||||
@ -47,7 +47,7 @@ verification conditions:
|
||||
|
||||
```yaml
|
||||
# Require that the result be status code 200-299 match regex "OK, account id is .*" in the body
|
||||
statements:
|
||||
ops:
|
||||
- get-from-google:
|
||||
method: GET
|
||||
uri: "https://google.com/"
|
||||
@ -64,7 +64,7 @@ parameters in a familiar and condensed form. A custom config parser makes
|
||||
this form available fo rhose who want to emulate a well-known pattern:
|
||||
|
||||
```yaml
|
||||
statements:
|
||||
ops:
|
||||
- s1: |
|
||||
GET https://google.com/ HTTP/1.1
|
||||
Content-Type: application/json
|
||||
@ -81,7 +81,7 @@ All request fields can be made dynamic with binding functions. To make a
|
||||
request that has all dynamic fields, you can do something like this:
|
||||
|
||||
```yaml
|
||||
statements:
|
||||
ops:
|
||||
- s1: |
|
||||
{method} {scheme}://{host}:{port}/{path}?{query} {version}
|
||||
Content-Type: {content_type}
|
||||
@ -95,7 +95,7 @@ interpreted internally as if you had configured your op template like
|
||||
this:
|
||||
|
||||
```yaml
|
||||
statements:
|
||||
ops:
|
||||
- method: { method }
|
||||
uri: { scheme }://{host}:{port}/{path}?{query}
|
||||
version: { version }
|
||||
@ -129,7 +129,6 @@ 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
|
||||
embedded sections which are contained within `URLENCODE[[` ... `]]`
|
||||
sections are preprocessed by the HTTP driver. This allows you to keep
|
||||
@ -207,10 +206,6 @@ undefined results.
|
||||
|
||||
## HTTP Activity Parameters
|
||||
|
||||
- **client_scope** - default: activity - One of activity, or thread. This
|
||||
controls how many clients instances you use with an HTTP activity. By
|
||||
default, all threads will use the same client instance.
|
||||
|
||||
- **follow_redirects** - default: normal - One of never, always, or
|
||||
normal. Normal redirects are those which do not redirect from HTTPS to
|
||||
HTTP.
|
@ -16,6 +16,7 @@
|
||||
|
||||
package io.nosqlbench.activitytype.cmds;
|
||||
|
||||
import io.nosqlbench.adapter.http.core.HttpFormatParser;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
@ -14,9 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.http;
|
||||
package io.nosqlbench.activitytype.cmds;
|
||||
|
||||
public class ReadyHttpOpTest {
|
||||
|
||||
public enum ClientScope {
|
||||
thread,
|
||||
activity
|
||||
}
|
@ -16,6 +16,9 @@
|
||||
|
||||
package io.nosqlbench.activitytype.http.statuscodes;
|
||||
|
||||
import io.nosqlbench.adapter.http.statuscodes.HttpStatusCodes;
|
||||
import io.nosqlbench.adapter.http.statuscodes.HttpStatusRanges;
|
||||
import io.nosqlbench.adapter.http.statuscodes.IetfStatusCode;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.adapter.http;
|
||||
|
||||
import io.nosqlbench.adapter.http.core.HttpOpMapper;
|
||||
import io.nosqlbench.adapter.http.core.HttpSpace;
|
||||
import io.nosqlbench.engine.api.activityconfig.StatementsLoader;
|
||||
import io.nosqlbench.engine.api.activityconfig.yaml.OpTemplate;
|
||||
import io.nosqlbench.engine.api.activityconfig.yaml.StmtsDocList;
|
||||
import io.nosqlbench.engine.api.activityimpl.uniform.DriverSpaceCache;
|
||||
import io.nosqlbench.engine.api.templating.ParsedOp;
|
||||
import io.nosqlbench.nb.api.config.standard.NBConfiguration;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class HttpOpMapperTest {
|
||||
|
||||
static NBConfiguration cfg;
|
||||
static HttpDriverAdapter adapter;
|
||||
static HttpOpMapper mapper;
|
||||
|
||||
@BeforeAll
|
||||
public static void initializeTestMapper() {
|
||||
cfg = HttpSpace.getConfigModel().apply(Map.of());
|
||||
adapter = new HttpDriverAdapter();
|
||||
adapter.applyConfig(cfg);
|
||||
DriverSpaceCache<? extends HttpSpace> cache = adapter.getSpaceCache();
|
||||
mapper = new HttpOpMapper(cfg, cache);
|
||||
}
|
||||
|
||||
private static ParsedOp parsedOpFor(String yaml) {
|
||||
StmtsDocList docs = StatementsLoader.loadString(yaml, Map.of());
|
||||
OpTemplate stmtDef = docs.getStmts().get(0);
|
||||
ParsedOp parsedOp = new ParsedOp(stmtDef, cfg, List.of(adapter.getPreprocessor()));
|
||||
return parsedOp;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnelineSpec() {
|
||||
ParsedOp pop = parsedOpFor("""
|
||||
ops:
|
||||
- s1: method=get uri=http://localhost/
|
||||
""");
|
||||
assertThat(pop.getDefinedNames()).containsAll(List.of("method", "uri"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRFCFormMinimal() {
|
||||
ParsedOp pop = parsedOpFor("""
|
||||
ops:
|
||||
- s1: get http://localhost/
|
||||
""");
|
||||
|
||||
assertThat(pop.getDefinedNames()).containsAll(List.of("method", "uri"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRFCFormVersioned() {
|
||||
ParsedOp pop = parsedOpFor("""
|
||||
ops:
|
||||
- s1: get http://localhost/ HTTP/1.1
|
||||
""");
|
||||
assertThat(pop.getDefinedNames()).containsAll(List.of("method", "uri", "version"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRFCFormHeaders() {
|
||||
ParsedOp pop = parsedOpFor("""
|
||||
ops:
|
||||
- s1: |
|
||||
get http://localhost/
|
||||
Content-Type: application/json
|
||||
""");
|
||||
assertThat(pop.getDefinedNames()).containsAll(List.of("method", "uri", "Content-Type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRFCFormBody() {
|
||||
ParsedOp pop = parsedOpFor("""
|
||||
statements:
|
||||
- s1: |
|
||||
get http://localhost/
|
||||
|
||||
body1
|
||||
""");
|
||||
|
||||
assertThat(pop.getDefinedNames()).containsAll(List.of("method", "uri", "body"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRFCAllValuesTemplated() {
|
||||
|
||||
// This can not be fully resolved in the unit testing context, but it could be
|
||||
// in the integrated testing context. It is sufficient to verify parsing here.
|
||||
ParsedOp pop = parsedOpFor("""
|
||||
statements:
|
||||
- s1: |
|
||||
{method} {scheme}://{host}/{path}?{query} {version}
|
||||
Header1: {header1val}
|
||||
|
||||
{body}
|
||||
|
||||
bindings:
|
||||
method: StaticStringMapper('test')
|
||||
scheme: StaticStringMapper('test')
|
||||
host: StaticStringMapper('test')
|
||||
path: StaticStringMapper('test')
|
||||
query: StaticStringMapper('test')
|
||||
version: StaticStringMapper('test')
|
||||
header1val: StaticStringMapper('test')
|
||||
body: StaticStringMapper('test')
|
||||
""");
|
||||
|
||||
System.out.println(pop);
|
||||
assertThat(pop.getDefinedNames()).containsAll(List.of(
|
||||
"method","uri","version","Header1","body"
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.cmds;
|
||||
|
||||
import io.nosqlbench.activitytype.http.async.HttpAsyncAction;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.util.function.LongFunction;
|
||||
|
||||
public class HttpAsyncOp {
|
||||
public final HttpAsyncAction action;
|
||||
public final LongFunction<? extends HttpOp> op;
|
||||
public final long cycle;
|
||||
|
||||
private final HttpOp httpOp;
|
||||
private final HttpClient client;
|
||||
|
||||
public HttpAsyncOp(HttpAsyncAction action, LongFunction<? extends HttpOp> op, long cycle, HttpClient client) {
|
||||
this.action = action;
|
||||
this.op = op;
|
||||
this.cycle = cycle;
|
||||
this.client = client;
|
||||
this.httpOp = op.apply(cycle);
|
||||
|
||||
}
|
||||
|
||||
public HttpOp getOp() {
|
||||
return httpOp;
|
||||
}
|
||||
|
||||
public HttpClient getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public HttpAsyncAction getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.cmds;
|
||||
|
||||
import io.nosqlbench.engine.api.activityimpl.uniform.flowtypes.Op;
|
||||
|
||||
import java.net.http.HttpRequest;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class HttpOp implements Op {
|
||||
|
||||
public final Pattern ok_status;
|
||||
public final Pattern ok_body;
|
||||
public final HttpRequest request;
|
||||
|
||||
public HttpOp(HttpRequest request, Pattern ok_status, Pattern ok_body) {
|
||||
this.request = request;
|
||||
this.ok_status = ok_status;
|
||||
this.ok_body = ok_body;
|
||||
}
|
||||
}
|
@ -1,151 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.cmds;
|
||||
|
||||
import io.nosqlbench.engine.api.activityconfig.yaml.OpTemplate;
|
||||
import io.nosqlbench.engine.api.activityimpl.BaseOpDispenser;
|
||||
import io.nosqlbench.engine.api.templating.CommandTemplate;
|
||||
import io.nosqlbench.nb.api.errors.BasicError;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ReadyHttpOp extends BaseOpDispenser<HttpOp> {
|
||||
|
||||
private final CommandTemplate propertyTemplate;
|
||||
public static final String DEFAULT_OK_BODY = ".+?";
|
||||
public static final String DEFAULT_OK_STATUS = "2..";
|
||||
|
||||
// only populated if there is no value which is an actual bindings template
|
||||
private final HttpOp cachedOp;
|
||||
|
||||
public ReadyHttpOp(OpTemplate stmtDef) {
|
||||
super(stmtDef);
|
||||
propertyTemplate = new CommandTemplate(stmtDef,
|
||||
List.of(
|
||||
HttpFormatParser::parseUrl,
|
||||
HttpFormatParser::parseInline
|
||||
)
|
||||
);
|
||||
|
||||
sanityCheckUri();
|
||||
if (propertyTemplate.isStatic()) {
|
||||
cachedOp = apply(0);
|
||||
} else {
|
||||
cachedOp = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// :/?#[]@ !$&'()*+,;=
|
||||
|
||||
/**
|
||||
* Try to catch situations in which the user put invalid characters in some part of the URI.
|
||||
* In this case, the only safe thing to try seems to be to automatically urldecode
|
||||
*/
|
||||
private void sanityCheckUri() {
|
||||
Map<String, String> command = propertyTemplate.getCommand(0L);
|
||||
if (command.containsKey("uri")) {
|
||||
String uriSpec = command.get("uri");
|
||||
URI uri = null;
|
||||
try {
|
||||
uri = new URI(uriSpec);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new BasicError(e.getMessage() + ", either use URLEncode in your bindings for values which could " +
|
||||
"contain invalid URI characters, or modify the static portions of your op template to use the" +
|
||||
" appropriate encodings.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpOp apply(long value) {
|
||||
|
||||
// If the request is invariant, simply return it, since it is thread-safe
|
||||
if (this.cachedOp != null) {
|
||||
return this.cachedOp;
|
||||
}
|
||||
|
||||
Map<String, String> cmd = propertyTemplate.getCommand(value);
|
||||
|
||||
HttpRequest.Builder builder = HttpRequest.newBuilder();
|
||||
|
||||
HttpRequest.BodyPublisher bodyPublisher = cmd.containsKey("body") ?
|
||||
HttpRequest.BodyPublishers.ofString(cmd.remove("body"))
|
||||
: HttpRequest.BodyPublishers.noBody();
|
||||
|
||||
String method = cmd.containsKey("method") ? cmd.remove("method") : "GET";
|
||||
|
||||
builder.method(method, bodyPublisher);
|
||||
|
||||
if (cmd.containsKey("version")) {
|
||||
String versionName = cmd.remove("version")
|
||||
.replaceAll("/1.1", "_1_1")
|
||||
.replaceAll("/2.0", "_2");
|
||||
HttpClient.Version version = HttpClient.Version.valueOf(versionName);
|
||||
builder.version(version);
|
||||
}
|
||||
|
||||
if (cmd.containsKey("uri")) {
|
||||
URI uri = URI.create(cmd.remove("uri"));
|
||||
builder.uri(uri);
|
||||
}
|
||||
|
||||
Pattern ok_status = Pattern.compile(Optional.ofNullable(cmd.remove("ok-status")).orElse(DEFAULT_OK_STATUS));
|
||||
Pattern ok_body = Optional.ofNullable(cmd.remove("ok-body")).map(Pattern::compile).orElse(null);
|
||||
|
||||
String timeoutStr = cmd.remove("timeout");
|
||||
if (timeoutStr != null) {
|
||||
builder.timeout(Duration.of(Long.parseLong(timeoutStr), ChronoUnit.MILLIS));
|
||||
}
|
||||
|
||||
// At this point, the only things left in the list must be headers,
|
||||
// but we check them for upper-case conventions as a sanity check for the user
|
||||
for (String headerName : cmd.keySet()) {
|
||||
if (headerName.charAt(0) >= 'A' && headerName.charAt(0) <= 'Z') {
|
||||
String headerValue = cmd.get(headerName);
|
||||
builder = builder.header(headerName, headerValue);
|
||||
} else {
|
||||
throw new BasicError("HTTP request parameter '" + headerName + "' was not recognized as a basic request parameter, and it is not capitalized to indicate that it is a header.");
|
||||
}
|
||||
}
|
||||
// cmd.clear();
|
||||
// if (cmd.size()>0) {
|
||||
// throw new BasicError("Some provided request fields were not used: " + cmd.toString());
|
||||
// }
|
||||
//
|
||||
HttpRequest request = builder.build();
|
||||
return new HttpOp(request, ok_status, ok_body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ReadyHttpOp{" +
|
||||
"template=" + propertyTemplate +
|
||||
", cachedOp=" + cachedOp +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -1,183 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.http;
|
||||
|
||||
import com.codahale.metrics.Timer;
|
||||
import io.nosqlbench.activitytype.cmds.HttpOp;
|
||||
import io.nosqlbench.engine.api.activityapi.core.SyncAction;
|
||||
import io.nosqlbench.engine.api.activityapi.errorhandling.modular.ErrorDetail;
|
||||
import io.nosqlbench.engine.api.activityapi.planning.OpSequence;
|
||||
import io.nosqlbench.engine.api.activityimpl.ActivityDef;
|
||||
import io.nosqlbench.engine.api.activityimpl.OpDispenser;
|
||||
import io.nosqlbench.nb.api.errors.BasicError;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.LongFunction;
|
||||
|
||||
|
||||
public class HttpAction implements SyncAction {
|
||||
|
||||
private final static Logger logger = LogManager.getLogger(HttpAction.class);
|
||||
|
||||
private final HttpActivity httpActivity;
|
||||
private final int slot;
|
||||
private int maxTries = 1;
|
||||
|
||||
private OpSequence<OpDispenser<? extends HttpOp>> sequencer;
|
||||
private HttpClient client;
|
||||
|
||||
private final HttpResponse.BodyHandler<String> bodyreader = HttpResponse.BodyHandlers.ofString();
|
||||
|
||||
public HttpAction(ActivityDef activityDef, int slot, HttpActivity httpActivity) {
|
||||
this.slot = slot;
|
||||
this.httpActivity = httpActivity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
this.sequencer = httpActivity.getSequencer();
|
||||
this.client = initClient(httpActivity.getClientScope());
|
||||
this.maxTries = httpActivity.getActivityDef().getParams().getOptionalInteger("maxtries").orElse(10);
|
||||
}
|
||||
|
||||
private HttpClient initClient(ClientScope clientScope) {
|
||||
return httpActivity.getClient().apply(Thread.currentThread());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int runCycle(long cycle) {
|
||||
int tries = 0;
|
||||
|
||||
// The request to be used must be constructed from the template each time.
|
||||
HttpOp httpOp = null;
|
||||
|
||||
// The bind timer captures all the time involved in preparing the
|
||||
// operation for execution, including data generation as well as
|
||||
// op construction
|
||||
try (Timer.Context bindTime = httpActivity.bindTimer.time()) {
|
||||
LongFunction<? extends HttpOp> readyOp = sequencer.apply(cycle);
|
||||
httpOp = readyOp.apply(cycle);
|
||||
} catch (Exception e) {
|
||||
if (httpActivity.isDiagnosticMode()) {
|
||||
if (httpOp != null) {
|
||||
httpActivity.console.summarizeRequest("ERRORED REQUEST", e, httpOp.request, System.out, cycle,
|
||||
System.nanoTime());
|
||||
} else {
|
||||
System.out.println("---- REQUEST was null");
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("while binding request in cycle " + cycle + ": " + e.getMessage(), e);
|
||||
} finally {
|
||||
}
|
||||
|
||||
int resultCode=0;
|
||||
|
||||
while (tries < maxTries) {
|
||||
tries++;
|
||||
|
||||
CompletableFuture<HttpResponse<String>> responseFuture;
|
||||
try (Timer.Context executeTime = httpActivity.executeTimer.time()) {
|
||||
responseFuture = client.sendAsync(httpOp.request, this.bodyreader);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("while waiting for response in cycle " + cycle + ":" + e.getMessage(), e);
|
||||
}
|
||||
|
||||
HttpResponse<String> response = null;
|
||||
long startat = System.nanoTime();
|
||||
Exception error = null;
|
||||
try {
|
||||
response = responseFuture.get(httpActivity.getTimeoutMillis(), TimeUnit.MILLISECONDS);
|
||||
httpActivity.statusCodeHisto.update(response.statusCode());
|
||||
if (httpOp.ok_status != null) {
|
||||
if (!httpOp.ok_status.matcher(String.valueOf(response.statusCode())).matches()) {
|
||||
throw new InvalidStatusCodeException(cycle, httpOp.ok_status, response.statusCode());
|
||||
}
|
||||
}
|
||||
if (httpOp.ok_body != null) {
|
||||
if (!httpOp.ok_body.matcher(response.body()).matches()) {
|
||||
throw new InvalidResponseBodyException(cycle, httpOp.ok_body, response.body());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
error = e;
|
||||
// error = new RuntimeException("while waiting for response in cycle " + cycleValue + ":" + e.getMessage(), e);
|
||||
} finally {
|
||||
long nanos = System.nanoTime() - startat;
|
||||
httpActivity.resultTimer.update(nanos, TimeUnit.NANOSECONDS);
|
||||
if (error == null) {
|
||||
httpActivity.resultSuccessTimer.update(nanos, TimeUnit.NANOSECONDS);
|
||||
}
|
||||
if (httpActivity.isDiagnosticMode()) {
|
||||
httpActivity.console.summarizeRequest("request", error, httpOp.request, System.out, cycle, nanos);
|
||||
if (response != null) {
|
||||
httpActivity.console.summarizeResponseChain(error, response, System.out, cycle, nanos);
|
||||
} else {
|
||||
System.out.println("---- RESPONSE was null");
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
// TODO: use this as a documented example for how to add error handling to a new activity
|
||||
if (error == null) {
|
||||
break; // break out of the tries loop without retrying, because there was no error
|
||||
} else {
|
||||
// count and log exception types
|
||||
ErrorDetail detail = httpActivity.getErrorHandler().handleError(error, cycle, nanos);
|
||||
resultCode=detail.resultCode;
|
||||
if (!detail.isRetryable()) {
|
||||
break; // break out of the tries loop without retrying, because the error handler said so
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
httpActivity.triesHisto.update(tries);
|
||||
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
private HttpRequest.BodyPublisher bodySourceFrom(Map<String, String> cmdMap) {
|
||||
if (cmdMap.containsKey("body")) {
|
||||
String body = cmdMap.remove("body");
|
||||
return HttpRequest.BodyPublishers.ofString(body);
|
||||
} else if (cmdMap.containsKey("file")) {
|
||||
try {
|
||||
String file = cmdMap.get("file");
|
||||
Path path = Path.of(file);
|
||||
return HttpRequest.BodyPublishers.ofFile(path);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new BasicError("Could not find file content for request at " + cmdMap.get("file"));
|
||||
}
|
||||
} else {
|
||||
return HttpRequest.BodyPublishers.noBody();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,147 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.http;
|
||||
|
||||
import com.codahale.metrics.Histogram;
|
||||
import com.codahale.metrics.Meter;
|
||||
import com.codahale.metrics.Timer;
|
||||
import io.nosqlbench.activitytype.cmds.HttpOp;
|
||||
import io.nosqlbench.activitytype.cmds.ReadyHttpOp;
|
||||
import io.nosqlbench.engine.api.activityapi.core.Activity;
|
||||
import io.nosqlbench.engine.api.activityapi.core.ActivityDefObserver;
|
||||
import io.nosqlbench.engine.api.activityapi.errorhandling.modular.NBErrorHandler;
|
||||
import io.nosqlbench.engine.api.activityapi.planning.OpSequence;
|
||||
import io.nosqlbench.engine.api.activityimpl.ActivityDef;
|
||||
import io.nosqlbench.engine.api.activityimpl.OpDispenser;
|
||||
import io.nosqlbench.engine.api.activityimpl.SimpleActivity;
|
||||
import io.nosqlbench.engine.api.metrics.ActivityMetrics;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class HttpActivity extends SimpleActivity implements Activity, ActivityDefObserver {
|
||||
private final static Logger logger = LogManager.getLogger(HttpActivity.class);
|
||||
private final ActivityDef activityDef;
|
||||
public HttpConsoleFormats console;
|
||||
|
||||
// Used when sclientScope == ClientScope.activity
|
||||
private HttpClient activityClient;
|
||||
private ClientScope clientScope = ClientScope.activity;
|
||||
|
||||
public Timer bindTimer;
|
||||
public Timer executeTimer;
|
||||
public Histogram triesHisto;
|
||||
public Timer resultTimer;
|
||||
public Meter rowCounter;
|
||||
public Histogram skippedTokens;
|
||||
public Timer resultSuccessTimer;
|
||||
public Histogram statusCodeHisto;
|
||||
|
||||
private OpSequence<OpDispenser<? extends HttpOp>> sequencer;
|
||||
private boolean diagnosticsEnabled;
|
||||
private long timeout = Long.MAX_VALUE;
|
||||
private NBErrorHandler errorhandler;
|
||||
|
||||
public HttpActivity(ActivityDef activityDef) {
|
||||
super(activityDef);
|
||||
this.activityDef = activityDef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initActivity() {
|
||||
super.initActivity();
|
||||
|
||||
bindTimer = ActivityMetrics.timer(activityDef, "bind",this.getHdrDigits());
|
||||
executeTimer = ActivityMetrics.timer(activityDef, "execute", this.getHdrDigits());
|
||||
resultTimer = ActivityMetrics.timer(activityDef, "result", this.getHdrDigits());
|
||||
triesHisto = ActivityMetrics.histogram(activityDef, "tries", this.getHdrDigits());
|
||||
rowCounter = ActivityMetrics.meter(activityDef, "rows");
|
||||
statusCodeHisto = ActivityMetrics.histogram(activityDef, "statuscode",this.getHdrDigits());
|
||||
skippedTokens = ActivityMetrics.histogram(activityDef, "skipped-tokens",this.getHdrDigits());
|
||||
resultSuccessTimer = ActivityMetrics.timer(activityDef, "result-success", this.getHdrDigits());
|
||||
this.sequencer = createOpSequence(ReadyHttpOp::new, false);
|
||||
setDefaultsFromOpSequence(sequencer);
|
||||
onActivityDefUpdate(activityDef);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onActivityDefUpdate(ActivityDef activityDef) {
|
||||
super.onActivityDefUpdate(activityDef);
|
||||
|
||||
this.console = getParams().getOptionalString("diag")
|
||||
.map(s -> HttpConsoleFormats.apply(s, this.console))
|
||||
.orElseGet(() -> HttpConsoleFormats.apply(null, null));
|
||||
|
||||
this.diagnosticsEnabled = console.isDiagnosticMode();
|
||||
|
||||
this.timeout = getParams().getOptionalLong("timeout").orElse(Long.MAX_VALUE);
|
||||
|
||||
getParams().getOptionalString("client_scope")
|
||||
.map(ClientScope::valueOf)
|
||||
.ifPresent(this::setClientScope);
|
||||
|
||||
}
|
||||
|
||||
public long getTimeoutMillis() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
private void setClientScope(ClientScope clientScope) {
|
||||
this.clientScope = clientScope;
|
||||
}
|
||||
|
||||
public ClientScope getClientScope() {
|
||||
return clientScope;
|
||||
}
|
||||
|
||||
public synchronized Function<Thread, HttpClient> getClient() {
|
||||
switch (getClientScope()) {
|
||||
case thread:
|
||||
return t -> newClient();
|
||||
case activity:
|
||||
if (this.activityClient == null) {
|
||||
this.activityClient = newClient();
|
||||
}
|
||||
return t -> this.activityClient;
|
||||
default:
|
||||
throw new RuntimeException("unable to recognize client scope: " + getClientScope());
|
||||
}
|
||||
}
|
||||
|
||||
public HttpClient newClient() {
|
||||
HttpClient.Builder builder = HttpClient.newBuilder();
|
||||
HttpClient.Redirect follow_redirects = getParams().getOptionalString("follow_redirects")
|
||||
.map(String::toUpperCase)
|
||||
.map(HttpClient.Redirect::valueOf)
|
||||
.map(r -> {
|
||||
logger.debug("follow_redirects=>" + r);
|
||||
return r;
|
||||
}).orElse(HttpClient.Redirect.NORMAL);
|
||||
builder = builder.followRedirects(follow_redirects);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public OpSequence<OpDispenser<? extends HttpOp>> getSequencer() {
|
||||
return sequencer;
|
||||
}
|
||||
|
||||
public boolean isDiagnosticMode() {
|
||||
return diagnosticsEnabled;
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.http;
|
||||
|
||||
import io.nosqlbench.engine.api.activityapi.core.ActionDispenser;
|
||||
import io.nosqlbench.engine.api.activityapi.core.ActivityType;
|
||||
import io.nosqlbench.engine.api.activityimpl.ActivityDef;
|
||||
import io.nosqlbench.nb.annotations.Service;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@Service(value = ActivityType.class, selector = "http")
|
||||
public class HttpActivityType implements ActivityType<HttpActivity> {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(HttpActivityType.class);
|
||||
|
||||
@Override
|
||||
public ActionDispenser getActionDispenser(HttpActivity activity) {
|
||||
if (activity.getParams().getOptionalString("async").isPresent()) {
|
||||
throw new RuntimeException("The async http driver is not online yet.");
|
||||
}
|
||||
return new HttpActionDispenser(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpActivity getActivity(ActivityDef activityDef) {
|
||||
return new HttpActivity(activityDef);
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.http;
|
||||
|
||||
public class ResponseError extends RuntimeException {
|
||||
public ResponseError(String s) {
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.http.async;
|
||||
|
||||
import io.nosqlbench.activitytype.cmds.HttpAsyncOp;
|
||||
import io.nosqlbench.activitytype.cmds.HttpOp;
|
||||
import io.nosqlbench.activitytype.http.HttpActivity;
|
||||
import io.nosqlbench.engine.api.activityapi.core.BaseAsyncAction;
|
||||
import io.nosqlbench.engine.api.activityapi.core.ops.fluent.opfacets.TrackedOp;
|
||||
import io.nosqlbench.engine.api.activityapi.planning.OpSequence;
|
||||
import io.nosqlbench.engine.api.activityimpl.OpDispenser;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.LongFunction;
|
||||
|
||||
public class HttpAsyncAction extends BaseAsyncAction<HttpAsyncOp, HttpActivity> {
|
||||
|
||||
private final static Logger logger = LogManager.getLogger(HttpAsyncAction.class);
|
||||
|
||||
private OpSequence<OpDispenser<? extends HttpOp>> sequencer;
|
||||
private HttpClient client;
|
||||
|
||||
private CompletableFuture<HttpResponse<Void>> future;
|
||||
|
||||
|
||||
public HttpAsyncAction(HttpActivity httpActivity, int slot) {
|
||||
super(httpActivity,slot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startOpCycle(TrackedOp<HttpAsyncOp> opc) {
|
||||
HttpAsyncOp opdata = opc.getOpData();
|
||||
HttpOp op = opdata.getOp();
|
||||
|
||||
opc.start();
|
||||
future = opdata.getAction().client.sendAsync(op.request, HttpResponse.BodyHandlers.discarding());
|
||||
}
|
||||
|
||||
public void init() {
|
||||
this.sequencer = activity.getSequencer();
|
||||
this.client = activity.getClient().apply(Thread.currentThread());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongFunction<HttpAsyncOp> getOpInitFunction() {
|
||||
return l -> {
|
||||
LongFunction<? extends HttpOp> readyHttpOp = sequencer.apply(l);
|
||||
return new HttpAsyncOp(this,readyHttpOp,l,client);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// @Override
|
||||
// public boolean enqueue(TrackedOp<HttpAsyncOp> opc) {
|
||||
// HttpAsyncOp opData = opc.getOpData();
|
||||
// opData.op.
|
||||
// return false;
|
||||
// }
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.cmds;
|
||||
|
||||
import io.nosqlbench.engine.api.activityconfig.StatementsLoader;
|
||||
import io.nosqlbench.engine.api.activityconfig.yaml.OpTemplate;
|
||||
import io.nosqlbench.engine.api.activityconfig.yaml.StmtsDocList;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class ReadyHttpOpTest {
|
||||
|
||||
@Test
|
||||
public void testOnelineSpec() {
|
||||
StmtsDocList docs = StatementsLoader.loadString("" +
|
||||
"statements:\n" +
|
||||
" - s1: method=get uri=http://localhost/\n",
|
||||
Map.of()
|
||||
);
|
||||
OpTemplate stmtDef = docs.getStmts().get(0);
|
||||
|
||||
ReadyHttpOp readyReq = new ReadyHttpOp(stmtDef);
|
||||
HttpOp staticReq = readyReq.apply(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRFCFormMinimal() {
|
||||
StmtsDocList docs = StatementsLoader.loadString("" +
|
||||
"statements:\n" +
|
||||
" - s1: get http://localhost/",
|
||||
Map.of()
|
||||
);
|
||||
OpTemplate stmtDef = docs.getStmts().get(0);
|
||||
|
||||
ReadyHttpOp readyReq = new ReadyHttpOp(stmtDef);
|
||||
HttpOp staticReq = readyReq.apply(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRFCFormVersioned() {
|
||||
StmtsDocList docs = StatementsLoader.loadString("" +
|
||||
"statements:\n" +
|
||||
" - s1: get http://localhost/ HTTP/1.1",
|
||||
Map.of()
|
||||
);
|
||||
OpTemplate stmtDef = docs.getStmts().get(0);
|
||||
|
||||
ReadyHttpOp readyReq = new ReadyHttpOp(stmtDef);
|
||||
HttpOp staticReq = readyReq.apply(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRFCFormHeaders() {
|
||||
StmtsDocList docs = StatementsLoader.loadString("" +
|
||||
"statements:\n" +
|
||||
" - s1: |\n" +
|
||||
" get http://localhost/\n" +
|
||||
" Content-Type: application/json" +
|
||||
"",
|
||||
Map.of()
|
||||
);
|
||||
OpTemplate stmtDef = docs.getStmts().get(0);
|
||||
|
||||
ReadyHttpOp readyReq = new ReadyHttpOp(stmtDef);
|
||||
HttpOp staticReq = readyReq.apply(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRFCFormBody() {
|
||||
StmtsDocList docs = StatementsLoader.loadString("" +
|
||||
"statements:\n" +
|
||||
" - s1: |\n" +
|
||||
" get http://localhost/\n" +
|
||||
" \n" +
|
||||
" body1",
|
||||
Map.of()
|
||||
);
|
||||
OpTemplate stmtDef = docs.getStmts().get(0);
|
||||
|
||||
ReadyHttpOp readyReq = new ReadyHttpOp(stmtDef);
|
||||
HttpOp staticReq = readyReq.apply(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRFCAllValuesTemplated() {
|
||||
|
||||
// This can not be fully resolved in the unit testing context, but it could be
|
||||
// in the integrated testing context. It is sufficient to verify parsing here.
|
||||
StmtsDocList docs = StatementsLoader.loadString("" +
|
||||
"statements:\n" +
|
||||
" - s1: |\n" +
|
||||
" {method} {scheme}://{host}/{path}?{query} {version}\n" +
|
||||
" Header1: {header1val}\n" +
|
||||
" \n" +
|
||||
" {body}\n" +
|
||||
"\n" +
|
||||
"bindings: \n" +
|
||||
" method: StaticString('test')\n" +
|
||||
" scheme: StaticString('test')\n" +
|
||||
" host: StaticString('test')\n" +
|
||||
" path: StaticString('test')\n" +
|
||||
" query: StaticString('test')\n" +
|
||||
" version: StaticString('test')\n" +
|
||||
" header1val: StaticString('test')\n" +
|
||||
" body: StaticString('test')\n",
|
||||
Map.of()
|
||||
);
|
||||
OpTemplate stmtDef = docs.getStmts().get(0);
|
||||
|
||||
Map<String, String> parse = HttpFormatParser.parseInline(stmtDef.getStmt().orElseThrow());
|
||||
assertThat(parse).containsAllEntriesOf(
|
||||
Map.of(
|
||||
"method", "{method}",
|
||||
"uri", "{scheme}://{host}/{path}?{query}",
|
||||
"version", "{version}",
|
||||
"Header1","{header1val}",
|
||||
"body","{body}"
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.http;
|
||||
|
||||
import io.nosqlbench.engine.api.activityapi.core.Action;
|
||||
import io.nosqlbench.engine.api.activityapi.core.ActionDispenser;
|
||||
import io.nosqlbench.engine.api.activityimpl.ActivityDef;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class HttpActivityTypeTest {
|
||||
@Test
|
||||
public void testHttpActivity() {
|
||||
HttpActivityType httpAt = new HttpActivityType();
|
||||
|
||||
ActivityDef ad = ActivityDef.parseActivityDef("driver=http; yaml=http-google.yaml; port=80; cycles=1;");
|
||||
HttpActivity httpActivity = httpAt.getActivity(ad);
|
||||
httpActivity.initActivity();
|
||||
ActionDispenser actionDispenser = httpAt.getActionDispenser(httpActivity);
|
||||
Action action = actionDispenser.getAction(1);
|
||||
}
|
||||
}
|
@ -70,6 +70,12 @@
|
||||
<version>4.17.15-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.nosqlbench</groupId>
|
||||
<artifactId>adapter-http</artifactId>
|
||||
<version>4.17.15-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
2
pom.xml
2
pom.xml
@ -54,6 +54,7 @@
|
||||
<module>adapters-api</module>
|
||||
|
||||
<!-- driver modules -->
|
||||
<module>adapter-http</module>
|
||||
<module>adapter-cqld4</module>
|
||||
<module>adapter-dynamodb</module>
|
||||
<module>adapter-diag</module>
|
||||
@ -85,7 +86,6 @@
|
||||
<modules>
|
||||
<module>nb</module>
|
||||
<module>driver-tcp</module>
|
||||
<module>driver-http</module>
|
||||
<module>driver-kafka</module>
|
||||
<module>driver-jmx</module>
|
||||
<module>driver-jdbc</module>
|
||||
|
Loading…
Reference in New Issue
Block a user