Scheme & Protocol
This commit is contained in:
committed by
Marcin Grzejszczak
parent
2121b0b1b4
commit
5e5782f736
@@ -41,6 +41,7 @@ class CustomModeGiven implements Given, BodyMethodVisitor, CustomModeAcceptor {
|
||||
new CustomModeRequestGiven(blockBuilder, generatedClassMetaData)));
|
||||
this.bodyGivens.addAll(
|
||||
Arrays.asList(new CustomModeMethodWithUrlGiven(blockBuilder, bodyParser),
|
||||
new CustomModeSchemeProtocolGiven(blockBuilder),
|
||||
new CustomModeHeadersGiven(blockBuilder),
|
||||
new CustomModeCookiesGiven(blockBuilder),
|
||||
new CustomModeBodyGiven(blockBuilder, generatedClassMetaData,
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.springframework.cloud.contract.verifier.builder;
|
||||
|
||||
import org.springframework.cloud.contract.spec.Contract;
|
||||
import org.springframework.cloud.contract.spec.internal.Request;
|
||||
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
|
||||
import org.springframework.cloud.contract.verifier.http.ContractVerifierHttpMetadata;
|
||||
|
||||
class CustomModeSchemeProtocolGiven implements Given {
|
||||
|
||||
private final BlockBuilder blockBuilder;
|
||||
|
||||
CustomModeSchemeProtocolGiven(BlockBuilder blockBuilder) {
|
||||
this.blockBuilder = blockBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor<Given> apply(SingleContractMetadata metadata) {
|
||||
Contract contract = metadata.getContract();
|
||||
ContractVerifierHttpMetadata httpMetadata = ContractVerifierHttpMetadata
|
||||
.fromMetadata(contract.getMetadata());
|
||||
this.blockBuilder
|
||||
.addIndented(".scheme(\"" + httpMetadata.getScheme().name() + "\")")
|
||||
.addEmptyLine();
|
||||
this.blockBuilder.addIndented(
|
||||
".protocol(\"" + httpMetadata.getProtocol().toString() + "\")");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(SingleContractMetadata metadata) {
|
||||
Request request = metadata.getContract().getRequest();
|
||||
return request != null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,12 +42,12 @@ public class ContractVerifierHttpMetadata implements SpringCloudContractMetadata
|
||||
/**
|
||||
* Scheme used for HTTP communication.
|
||||
*/
|
||||
private Scheme scheme;
|
||||
private Scheme scheme = Scheme.HTTP;
|
||||
|
||||
/**
|
||||
* Protocol used for HTTP communication.
|
||||
*/
|
||||
private Protocol protocol;
|
||||
private Protocol protocol = Protocol.HTTP_1_1;
|
||||
|
||||
@NonNull
|
||||
public static ContractVerifierHttpMetadata fromMetadata(
|
||||
@@ -117,7 +117,7 @@ public class ContractVerifierHttpMetadata implements SpringCloudContractMetadata
|
||||
}
|
||||
|
||||
/**
|
||||
* Taken from OKHttp's Protocol.
|
||||
* Taken from OKHttp's Protocol {@link okhttp3.Protocol}.
|
||||
*/
|
||||
public enum Protocol {
|
||||
|
||||
|
||||
@@ -18,10 +18,10 @@ package org.springframework.cloud.contract.verifier.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -33,8 +33,8 @@ import okhttp3.RequestBody;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* {@link HttpVerifier} implementation that uses {@link OkHttpClient}.
|
||||
* Has an inbuilt support for GRPC.
|
||||
* {@link HttpVerifier} implementation that uses {@link OkHttpClient}. Has an inbuilt
|
||||
* support for GRPC.
|
||||
*
|
||||
* Warning! This API is experimental and can change in time.
|
||||
*
|
||||
@@ -43,24 +43,36 @@ import org.jetbrains.annotations.Nullable;
|
||||
*/
|
||||
public class OkHttpHttpVerifier implements HttpVerifier {
|
||||
|
||||
private final String url;
|
||||
private final String hostAndPort;
|
||||
|
||||
public OkHttpHttpVerifier(String url) {
|
||||
this.url = url;
|
||||
/**
|
||||
* @param hostAndPort - don't pass the scheme, it will be resolved from
|
||||
* {@link Request#scheme()}. E.g. pass {@code localhost:1234}.
|
||||
*/
|
||||
public OkHttpHttpVerifier(String hostAndPort) {
|
||||
this.hostAndPort = hostAndPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response exchange(Request request) {
|
||||
String requestContentType = request.contentType();
|
||||
// TODO: Resolve protocol and scheme from contract?
|
||||
OkHttpClient client = new OkHttpClient.Builder()
|
||||
.protocols(protocols(requestContentType)).build();
|
||||
okhttp3.Request req = new okhttp3.Request.Builder()
|
||||
.url(this.url + (request.path().startsWith("/") ? request.path()
|
||||
: "/" + request.path()))
|
||||
.method(request.method().name(), requestBody(request, requestContentType))
|
||||
.headers(Headers.of(stringTyped(request.headers()))) // TODO: Add cookies
|
||||
.protocols(Collections
|
||||
.singletonList(toProtocol(request.protocol().toString())))
|
||||
.build();
|
||||
Map<String, String> headers = stringTyped(request.headers());
|
||||
if (!request.cookies().isEmpty()) {
|
||||
headers.put("Set-Cookie",
|
||||
request.cookies().entrySet().stream()
|
||||
.map(e -> e.getKey() + "=" + e.getValue().toString())
|
||||
.collect(Collectors.joining(";")));
|
||||
}
|
||||
okhttp3.Request req = new okhttp3.Request.Builder()
|
||||
.url(request.scheme().name().toLowerCase() + ":" + this.hostAndPort
|
||||
+ (request.path().startsWith("/") ? request.path()
|
||||
: "/" + request.path()))
|
||||
.method(request.method().name(), requestBody(request, requestContentType))
|
||||
.headers(Headers.of(headers)).build();
|
||||
try (okhttp3.Response res = client.newCall(req).execute()) {
|
||||
return response(res);
|
||||
}
|
||||
@@ -69,29 +81,27 @@ public class OkHttpHttpVerifier implements HttpVerifier {
|
||||
}
|
||||
}
|
||||
|
||||
private List<Protocol> protocols(String requestContentType) {
|
||||
if (this.url.startsWith("https")) {
|
||||
return Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1);
|
||||
private Protocol toProtocol(String string) {
|
||||
try {
|
||||
return Protocol.get(string);
|
||||
}
|
||||
else if (isGrpc(requestContentType)) {
|
||||
return Collections.singletonList(Protocol.H2_PRIOR_KNOWLEDGE);
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
return Collections.singletonList(Protocol.HTTP_1_1);
|
||||
}
|
||||
|
||||
private Response response(okhttp3.Response res) throws IOException {
|
||||
byte[] responseBody = responseBody(res);
|
||||
// String contentType = res.headers().get("Content-Type");
|
||||
// TODO: Response body in the test / contract should already be properly encoded
|
||||
// if (contentType != null && isGrpc(contentType) && responseBody != null) {
|
||||
// responseBody = grpcResponseBody(responseBody);
|
||||
// }
|
||||
return Response.builder().body(responseBody).statusCode(res.code())
|
||||
.headers(withSingleHeader(res))
|
||||
// TODO: Add cookies
|
||||
// .cookies(res.headers().values("Set-Cookie").stream().map(s ->
|
||||
// s.split(";")).flatMap(Arrays::stream).collect(Collectors.toMap(o -> o.
|
||||
// , e -> e.getValue().get(0), (a,b) -> a, HashMap::new))))
|
||||
.headers(withSingleHeader(res)).cookies(res.headers().values("Set-Cookie")
|
||||
.stream().flatMap(s -> Arrays.stream(s.split(";"))).map(s -> {
|
||||
String[] singleCookie = s.split("=");
|
||||
return new AbstractMap.SimpleEntry<>(singleCookie[0],
|
||||
singleCookie.length > 1 ? singleCookie[1] : "");
|
||||
})
|
||||
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey,
|
||||
AbstractMap.SimpleEntry::getValue, (a, b) -> a,
|
||||
HashMap::new)))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@ import org.springframework.cloud.contract.spec.internal.HttpMethods;
|
||||
*/
|
||||
public class Request {
|
||||
|
||||
private final ContractVerifierHttpMetadata.Protocol protocol;
|
||||
|
||||
private final ContractVerifierHttpMetadata.Scheme scheme;
|
||||
|
||||
private final HttpMethods.HttpMethod method;
|
||||
|
||||
private final String path;
|
||||
@@ -42,8 +46,12 @@ public class Request {
|
||||
|
||||
private final Map<String, Object> cookies;
|
||||
|
||||
Request(HttpMethods.HttpMethod method, String path, Body body,
|
||||
Map<String, Object> headers, Map<String, Object> cookies) {
|
||||
Request(ContractVerifierHttpMetadata.Protocol protocol,
|
||||
ContractVerifierHttpMetadata.Scheme scheme, HttpMethods.HttpMethod method,
|
||||
String path, Body body, Map<String, Object> headers,
|
||||
Map<String, Object> cookies) {
|
||||
this.protocol = protocol;
|
||||
this.scheme = scheme;
|
||||
this.method = method;
|
||||
this.path = path;
|
||||
this.body = body;
|
||||
@@ -64,6 +72,20 @@ public class Request {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link ContractVerifierHttpMetadata.Protocol}
|
||||
*/
|
||||
public ContractVerifierHttpMetadata.Protocol protocol() {
|
||||
return this.protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link ContractVerifierHttpMetadata.Scheme}
|
||||
*/
|
||||
public ContractVerifierHttpMetadata.Scheme scheme() {
|
||||
return this.scheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HTTP method
|
||||
*/
|
||||
@@ -195,6 +217,10 @@ public class Request {
|
||||
|
||||
final String path;
|
||||
|
||||
ContractVerifierHttpMetadata.Protocol protocol = ContractVerifierHttpMetadata.Protocol.HTTP_1_1;
|
||||
|
||||
ContractVerifierHttpMetadata.Scheme scheme = ContractVerifierHttpMetadata.Scheme.HTTP;
|
||||
|
||||
Body body;
|
||||
|
||||
Map<String, Object> headers = new HashMap<>();
|
||||
@@ -206,6 +232,24 @@ public class Request {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scheme text representation of a scheme
|
||||
* @return builder
|
||||
*/
|
||||
public Request.Builder scheme(String scheme) {
|
||||
this.scheme = ContractVerifierHttpMetadata.Scheme.fromString(scheme);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param protocol text representation of a protocol
|
||||
* @return builder
|
||||
*/
|
||||
public Request.Builder protocol(String protocol) {
|
||||
this.protocol = ContractVerifierHttpMetadata.Protocol.fromString(protocol);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param body HTTP body
|
||||
* @return builder
|
||||
@@ -247,8 +291,8 @@ public class Request {
|
||||
* @return built {@link Request}
|
||||
*/
|
||||
public Request build() {
|
||||
return new Request(this.method, this.path, this.body, this.headers,
|
||||
this.cookies);
|
||||
return new Request(this.protocol, this.scheme, this.method, this.path,
|
||||
this.body, this.headers, this.cookies);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user