From 3c8b712aa288459462554b4f55e3b04d3e29b702 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 18 Apr 2017 12:06:09 +0200 Subject: [PATCH 01/11] Made BlockBuilder public it's a useful class that can be used by different test generators fixes #254 --- .../verifier/builder/BlockBuilder.groovy | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/BlockBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/BlockBuilder.groovy index de3f392561..2f2ef6da5c 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/BlockBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/BlockBuilder.groovy @@ -27,7 +27,6 @@ import groovy.transform.PackageScope * * @since 1.0.0 */ -@PackageScope @CompileStatic class BlockBuilder { @@ -43,21 +42,33 @@ class BlockBuilder { builder = new StringBuilder() } + /** + * Adds indents to start a new block + */ BlockBuilder startBlock() { indents++ return this } + /** + * Ends block by removing indents + */ BlockBuilder endBlock() { indents-- return this } + /** + * Creates a block and adds indents + */ BlockBuilder indent() { startBlock().startBlock() return this } + /** + * Removes indents and closes the block + */ BlockBuilder unindent() { endBlock().endBlock() return this @@ -81,6 +92,7 @@ class BlockBuilder { } } + @PackageScope BlockBuilder addBlock(MethodBuilder methodBuilder) { startBlock() methodBuilder.appendTo(this) @@ -89,6 +101,11 @@ class BlockBuilder { return this } + /** + * Adds the given text at the end of the line + * + * @return updated BlockBuilder + */ BlockBuilder addAtTheEnd(String toAdd) { String lastChar = builder.charAt(builder.length() - 1) as String String secondLastChar = builder.length() >= 2 ? builder.charAt(builder.length() - 2) as String : "" @@ -114,6 +131,12 @@ class BlockBuilder { return character == "{" || character == toAdd } + /** + * Updates the current text with the provided one + * + * @param contents - text to replace the current content with + * @return updated Block Builder + */ BlockBuilder updateContents(String contents) { this.builder.replace(0, this.builder.length(), contents) return this From 7c8a8b4d333dcdb5235ad67fb1d8660825bf0c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kubry=C5=84ski?= Date: Tue, 18 Apr 2017 14:17:35 +0200 Subject: [PATCH 02/11] Simplify samples used in documentation (#268) --- .../com/example/loan/LoanApplicationService.java | 10 ++++------ .../com/example/fraud/FraudDetectionController.java | 7 +------ .../java/com/example/fraud/FraudNameController.java | 6 +----- .../java/com/example/fraud/FraudStatsController.java | 10 ++-------- .../contracts/fraud/shouldMarkClientAsFraud.groovy | 12 ++++++------ .../fraud/shouldMarkClientAsNotFraud.groovy | 4 ++-- .../contracts/fraud/shouldReturnFraudStats.groovy | 4 ++-- .../fraudname/shouldReturnAFraudForTheName.groovy | 2 +- .../fraudname/shouldReturnNonFraudForTheName.groovy | 2 +- 9 files changed, 20 insertions(+), 37 deletions(-) diff --git a/samples/standalone/dsl/http-client/src/main/java/com/example/loan/LoanApplicationService.java b/samples/standalone/dsl/http-client/src/main/java/com/example/loan/LoanApplicationService.java index c91df67aea..cf26b27f35 100644 --- a/samples/standalone/dsl/http-client/src/main/java/com/example/loan/LoanApplicationService.java +++ b/samples/standalone/dsl/http-client/src/main/java/com/example/loan/LoanApplicationService.java @@ -5,6 +5,7 @@ import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; @@ -20,9 +21,6 @@ import com.example.loan.model.Response; @Service public class LoanApplicationService { - private static final String FRAUD_SERVICE_JSON_VERSION_1 = - "application/vnd.fraud.v1+json"; - private final RestTemplate restTemplate; private int port = 6565; @@ -45,7 +43,7 @@ public class LoanApplicationService { private FraudServiceResponse sendRequestToFraudDetectionService( FraudServiceRequest request) { HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.add(HttpHeaders.CONTENT_TYPE, FRAUD_SERVICE_JSON_VERSION_1); + httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); // tag::client_call_server[] ResponseEntity response = @@ -70,7 +68,7 @@ public class LoanApplicationService { public int countAllFrauds() { HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.add(HttpHeaders.CONTENT_TYPE, FRAUD_SERVICE_JSON_VERSION_1); + httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); ResponseEntity response = restTemplate.exchange("http://localhost:" + port + "/frauds", HttpMethod.GET, new HttpEntity<>(httpHeaders), @@ -80,7 +78,7 @@ public class LoanApplicationService { public int countDrunks() { HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.add(HttpHeaders.CONTENT_TYPE, FRAUD_SERVICE_JSON_VERSION_1); + httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); ResponseEntity response = restTemplate.exchange("http://localhost:" + port + "/drunks", HttpMethod.GET, new HttpEntity<>(httpHeaders), diff --git a/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudDetectionController.java b/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudDetectionController.java index d172969868..764f8a6e96 100644 --- a/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudDetectionController.java +++ b/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudDetectionController.java @@ -15,17 +15,12 @@ import static org.springframework.web.bind.annotation.RequestMethod.PUT; @RestController public class FraudDetectionController { - private static final String FRAUD_SERVICE_JSON_VERSION_1 = "application/vnd.fraud.v1+json"; private static final String NO_REASON = null; private static final String AMOUNT_TOO_HIGH = "Amount too high"; private static final BigDecimal MAX_AMOUNT = new BigDecimal("5000"); // tag::server_api[] - @RequestMapping( - value = "/fraudcheck", - method = PUT, - consumes = FRAUD_SERVICE_JSON_VERSION_1, - produces = FRAUD_SERVICE_JSON_VERSION_1) + @RequestMapping(value = "/fraudcheck", method = PUT) public FraudCheckResult fraudCheck(@RequestBody FraudCheck fraudCheck) { // end::server_api[] // tag::new_impl[] diff --git a/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudNameController.java b/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudNameController.java index ea6ff8f2c0..4ba6881594 100644 --- a/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudNameController.java +++ b/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudNameController.java @@ -10,17 +10,13 @@ import org.springframework.web.bind.annotation.RestController; @RestController class FraudNameController { - private static final String FRAUD_SERVICE_JSON_VERSION_1 = "application/vnd.fraud.v1+json"; - private final FraudVerifier fraudVerifier; FraudNameController(FraudVerifier fraudVerifier) { this.fraudVerifier = fraudVerifier; } - @PutMapping( - value = "/frauds/name", - produces = FRAUD_SERVICE_JSON_VERSION_1) + @PutMapping(value = "/frauds/name") public NameResponse checkByName(@RequestBody NameRequest request) { boolean fraud = this.fraudVerifier.isFraudByName(request.getName()); if (fraud) { diff --git a/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudStatsController.java b/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudStatsController.java index ab8d933814..ac9340611c 100644 --- a/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudStatsController.java +++ b/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudStatsController.java @@ -6,24 +6,18 @@ import org.springframework.web.bind.annotation.RestController; @RestController public class FraudStatsController { - private static final String FRAUD_SERVICE_JSON_VERSION_1 = "application/vnd.fraud.v1+json"; - private final StatsProvider statsProvider; public FraudStatsController(StatsProvider statsProvider) { this.statsProvider = statsProvider; } - @GetMapping( - value = "/frauds", - produces = FRAUD_SERVICE_JSON_VERSION_1) + @GetMapping(value = "/frauds") public Response countAllFrauds() { return new Response(this.statsProvider.count(FraudType.ALL)); } - @GetMapping( - value = "/drunks", - produces = FRAUD_SERVICE_JSON_VERSION_1) + @GetMapping(value = "/drunks") public Response countAllDrunks() { return new Response(this.statsProvider.count(FraudType.DRUNKS)); } diff --git a/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.groovy b/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.groovy index e798cdc7eb..915ae084a7 100644 --- a/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.groovy +++ b/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.groovy @@ -9,7 +9,7 @@ org.springframework.cloud.contract.spec.Contract.make { loanAmount: 99999 ]) headers { // (5) - contentType('application/vnd.fraud.v1+json') + contentType('application/json') } } response { // (6) @@ -19,7 +19,7 @@ org.springframework.cloud.contract.spec.Contract.make { rejectionReason: "Amount too high" ]) headers { // (9) - contentType('application/vnd.fraud.v1+json') + contentType('application/json') } } } @@ -42,12 +42,12 @@ From the Consumer perspective, when shooting a request in the integration test: (4) - with the JSON body that * has a field `clientId` that matches a regular expression `[0-9]{10}` * has a field `loanAmount` that is equal to `99999` -(5) - with header `Content-Type` equal to `application/vnd.fraud.v1+json` +(5) - with header `Content-Type` equal to `application/json` (6) - then the response will be sent with (7) - status equal `200` (8) - and JSON body equal to { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" } -(9) - with header `Content-Type` equal to `application/vnd.fraud.v1+json` +(9) - with header `Content-Type` equal to `application/json` From the Producer perspective, in the autogenerated producer-side test: @@ -57,10 +57,10 @@ From the Producer perspective, in the autogenerated producer-side test: (4) - with the JSON body that * has a field `clientId` that will have a generated value that matches a regular expression `[0-9]{10}` * has a field `loanAmount` that is equal to `99999` -(5) - with header `Content-Type` equal to `application/vnd.fraud.v1+json` +(5) - with header `Content-Type` equal to `application/json` (6) - then the test will assert if the response has been sent with (7) - status equal `200` (8) - and JSON body equal to { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" } -(9) - with header `Content-Type` matching `application/vnd.fraud.v1+json.*` +(9) - with header `Content-Type` matching `application/json.*` */ \ No newline at end of file diff --git a/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsNotFraud.groovy b/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsNotFraud.groovy index c3dce3f32d..a04fe8447f 100644 --- a/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsNotFraud.groovy +++ b/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsNotFraud.groovy @@ -12,7 +12,7 @@ org.springframework.cloud.contract.spec.Contract.make { """ ) headers { - contentType("application/vnd.fraud.v1+json") + contentType("application/json") } } @@ -23,7 +23,7 @@ org.springframework.cloud.contract.spec.Contract.make { rejectionReason: $(consumer(null), producer(execute('assertThatRejectionReasonIsNull($it)'))) ) headers { - contentType("application/vnd.fraud.v1+json") + contentType("application/json") } } diff --git a/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldReturnFraudStats.groovy b/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldReturnFraudStats.groovy index a6ed5a97d7..1b95f5e19f 100644 --- a/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldReturnFraudStats.groovy +++ b/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldReturnFraudStats.groovy @@ -15,7 +15,7 @@ import org.springframework.cloud.contract.spec.Contract count: 200 ]) headers { - contentType("application/vnd.fraud.v1+json") + contentType("application/json") } } }, @@ -30,7 +30,7 @@ import org.springframework.cloud.contract.spec.Contract count: 100 ]) headers { - contentType("application/vnd.fraud.v1+json") + contentType("application/json") } } } diff --git a/samples/standalone/dsl/http-server/src/test/resources/contracts/fraudname/shouldReturnAFraudForTheName.groovy b/samples/standalone/dsl/http-server/src/test/resources/contracts/fraudname/shouldReturnAFraudForTheName.groovy index f2f96c2676..31c3d0e215 100644 --- a/samples/standalone/dsl/http-server/src/test/resources/contracts/fraudname/shouldReturnAFraudForTheName.groovy +++ b/samples/standalone/dsl/http-server/src/test/resources/contracts/fraudname/shouldReturnAFraudForTheName.groovy @@ -10,7 +10,7 @@ org.springframework.cloud.contract.spec.Contract.make { name: "fraud" ]) headers { - contentType("application/vnd.fraud.v1+json") + contentType("application/json") } } response { diff --git a/samples/standalone/dsl/http-server/src/test/resources/contracts/fraudname/shouldReturnNonFraudForTheName.groovy b/samples/standalone/dsl/http-server/src/test/resources/contracts/fraudname/shouldReturnNonFraudForTheName.groovy index eddea317a7..ce9a92a533 100644 --- a/samples/standalone/dsl/http-server/src/test/resources/contracts/fraudname/shouldReturnNonFraudForTheName.groovy +++ b/samples/standalone/dsl/http-server/src/test/resources/contracts/fraudname/shouldReturnNonFraudForTheName.groovy @@ -8,7 +8,7 @@ org.springframework.cloud.contract.spec.Contract.make { name: $(anyAlphaUnicode()) ]) headers { - contentType("application/vnd.fraud.v1+json") + contentType("application/json") } } response { From 235281ce9fc9e2480bc628b57ffa7df488f75587 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 21 Apr 2017 16:09:08 +0100 Subject: [PATCH 03/11] Add @AutoConfigureHttpClient for SSL validation error handling For tests only users can add this annotation to make the rest template in their app ignore SSL validation. --- .../main/asciidoc/spring-cloud-wiremock.adoc | 43 ++++++++++++ .../com/example/WiremockTestsApplication.java | 5 +- .../WiremockHttpsServerApplicationTests.java | 30 ++++---- .../com/example/WiremockTestsApplication.java | 5 +- .../WiremockHttpsServerApplicationTests.java | 2 + .../wiremock/AutoConfigureHttpClient.java | 41 +++++++++++ .../wiremock/AutoConfigureWireMock.java | 1 + .../WireMockRestTemplateConfiguration.java | 68 +++++++++++++++++++ .../contract/wiremock/WireMockSpring.java | 5 +- .../main/resources/META-INF/spring.factories | 2 +- 10 files changed, 181 insertions(+), 21 deletions(-) create mode 100644 spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/AutoConfigureHttpClient.java create mode 100644 spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockRestTemplateConfiguration.java diff --git a/docs/src/main/asciidoc/spring-cloud-wiremock.adoc b/docs/src/main/asciidoc/spring-cloud-wiremock.adoc index 087d19afe2..8e80571029 100644 --- a/docs/src/main/asciidoc/spring-cloud-wiremock.adoc +++ b/docs/src/main/asciidoc/spring-cloud-wiremock.adoc @@ -94,6 +94,49 @@ include::{doc_samples}/src/test/java/com/example/WiremockForDocsClassRuleTests.j The use `@ClassRule` means that the server will shut down after all the methods in this class. +== Relaxed SSL Validation for Rest Template + +WireMock allows you to stub a "secure" server with an "https" URL protocol. If your application wants to +contact that stub server in an integration test, then it will find that the SSL certificates are not +valid (it's the usual problem with self-installed certificates). The best option is often to just +re-configure the client to use "http", but if that's not open to you then you can ask Spring to configure +an HTTP client that ignores SSL validation errors (just for tests). + +To make this work with minimum fuss you need to be using the Spring Boot `RestTemplateBuilder` in your app, +e.g. + +[source,java,indent=0] +---- + @Bean + public RestTemplate restTemplate(RestTemplateBuilder builder) { + return builder.build(); + } +---- + +This is because the builder is passed through callbacks to initalize it, so the SSL validation can be set up +in the client at that point. This will happen automatically in your test if you are using the +`@AutoConfigureWireMock` annotation (or the stub runner). If you are using the JUnit `@Rule` approach you need +to add the `@AutoConfigureHttpClient` annotation as well: + +[source,java,indent=0] +---- +@RunWith(SpringRunner.class) +@SpringBootTest("app.baseUrl=https://localhost:6443") +@AutoConfigureHttpClient +public class WiremockHttpsServerApplicationTests { + + @ClassRule + public static WireMockClassRule wiremock = new WireMockClassRule( + WireMockSpring.options().httpsPort(6443)); +... +} +---- + +If you are using `spring-boot-starter-test` then you will have the Apache HTTP client on the classpath and it will +be selected by the `RestTemplateBuilder` and configured to ignore SSL errors. If you are using the default `java.net` +client you don't need the annotation (but it won't do any harm). There is no support currently for other clients, but +it may be added in future releases. + == WireMock and Spring MVC Mocks Spring Cloud Contract provides a convenience class that can load JSON WireMock stubs into a diff --git a/samples/wiremock-jetty/src/main/java/com/example/WiremockTestsApplication.java b/samples/wiremock-jetty/src/main/java/com/example/WiremockTestsApplication.java index dbf42f24cc..0608f2f338 100644 --- a/samples/wiremock-jetty/src/main/java/com/example/WiremockTestsApplication.java +++ b/samples/wiremock-jetty/src/main/java/com/example/WiremockTestsApplication.java @@ -3,6 +3,7 @@ package com.example; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestMapping; @@ -13,8 +14,8 @@ import org.springframework.web.client.RestTemplate; public class WiremockTestsApplication { @Bean - public RestTemplate restTemplate() { - return new RestTemplate(); + public RestTemplate restTemplate(RestTemplateBuilder builder) { + return builder.build(); } public static void main(String[] args) { diff --git a/samples/wiremock-jetty/src/test/java/com/example/WiremockHttpsServerApplicationTests.java b/samples/wiremock-jetty/src/test/java/com/example/WiremockHttpsServerApplicationTests.java index e700ce0211..93b83c12d5 100644 --- a/samples/wiremock-jetty/src/test/java/com/example/WiremockHttpsServerApplicationTests.java +++ b/samples/wiremock-jetty/src/test/java/com/example/WiremockHttpsServerApplicationTests.java @@ -1,26 +1,28 @@ package com.example; +import com.github.tomakehurst.wiremock.junit.WireMockClassRule; + +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.contract.wiremock.AutoConfigureHttpClient; +import org.springframework.cloud.contract.wiremock.WireMockSpring; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; + import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static org.assertj.core.api.Assertions.assertThat; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.contract.wiremock.WireMockSpring; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit4.SpringRunner; - -import com.github.tomakehurst.wiremock.junit.WireMockClassRule; - - @RunWith(SpringRunner.class) @SpringBootTest("app.baseUrl=https://localhost:8443") @DirtiesContext +@AutoConfigureHttpClient public class WiremockHttpsServerApplicationTests { @ClassRule @@ -32,8 +34,8 @@ public class WiremockHttpsServerApplicationTests { @Test public void contextLoads() throws Exception { - stubFor(get(urlEqualTo("/resource")) - .willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!"))); + stubFor(get(urlEqualTo("/resource")).willReturn(aResponse() + .withHeader("Content-Type", "text/plain").withBody("Hello World!"))); assertThat(this.service.go()).isEqualTo("Hello World!"); } diff --git a/samples/wiremock-tomcat/src/main/java/com/example/WiremockTestsApplication.java b/samples/wiremock-tomcat/src/main/java/com/example/WiremockTestsApplication.java index fa59698f17..926417a038 100644 --- a/samples/wiremock-tomcat/src/main/java/com/example/WiremockTestsApplication.java +++ b/samples/wiremock-tomcat/src/main/java/com/example/WiremockTestsApplication.java @@ -3,6 +3,7 @@ package com.example; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestMapping; @@ -13,8 +14,8 @@ import org.springframework.web.client.RestTemplate; public class WiremockTestsApplication { @Bean - public RestTemplate restTemplate() { - return new RestTemplate(); + public RestTemplate restTemplate(RestTemplateBuilder builder) { + return builder.build(); } public static void main(String[] args) { diff --git a/samples/wiremock-tomcat/src/test/java/com/example/WiremockHttpsServerApplicationTests.java b/samples/wiremock-tomcat/src/test/java/com/example/WiremockHttpsServerApplicationTests.java index add1d311fe..9e30002fdf 100644 --- a/samples/wiremock-tomcat/src/test/java/com/example/WiremockHttpsServerApplicationTests.java +++ b/samples/wiremock-tomcat/src/test/java/com/example/WiremockHttpsServerApplicationTests.java @@ -11,6 +11,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.contract.wiremock.AutoConfigureHttpClient; import org.springframework.cloud.contract.wiremock.WireMockSpring; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; @@ -21,6 +22,7 @@ import com.github.tomakehurst.wiremock.junit.WireMockClassRule; @RunWith(SpringRunner.class) @SpringBootTest("app.baseUrl=https://localhost:6443") @DirtiesContext +@AutoConfigureHttpClient public class WiremockHttpsServerApplicationTests { @ClassRule diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/AutoConfigureHttpClient.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/AutoConfigureHttpClient.java new file mode 100644 index 0000000000..48f3e9d60b --- /dev/null +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/AutoConfigureHttpClient.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012-2015 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 + * + * 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 org.springframework.cloud.contract.wiremock; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.test.autoconfigure.properties.PropertyMapping; +import org.springframework.context.annotation.Import; + +/** + * Annotation for test classes that want to install a RestTemplateCustomizer that sets up + * a Spring Boot app to ignore SSL errors. Use only in tests! + * + * @author Dave Syer + * + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Import(WireMockRestTemplateConfiguration.class) +@PropertyMapping("wiremock.server") +public @interface AutoConfigureHttpClient { +} diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMock.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMock.java index fb8db93eb1..b1337ba0c3 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMock.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMock.java @@ -39,6 +39,7 @@ import org.springframework.context.annotation.Import; @Documented @Import(WireMockConfiguration.class) @PropertyMapping("wiremock.server") +@AutoConfigureHttpClient public @interface AutoConfigureWireMock { int port() default 8080; diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockRestTemplateConfiguration.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockRestTemplateConfiguration.java new file mode 100644 index 0000000000..c543b1a556 --- /dev/null +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockRestTemplateConfiguration.java @@ -0,0 +1,68 @@ +/* + * Copyright 2016-2017 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 + * + * 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 org.springframework.cloud.contract.wiremock; + +import org.apache.http.client.HttpClient; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustSelfSignedStrategy; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContextBuilder; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.web.client.RestTemplateCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +/** + * @author Dave Syer + * + */ +@Configuration +public class WireMockRestTemplateConfiguration { + + @Bean + @ConditionalOnClass(SSLContextBuilder.class) + public RestTemplateCustomizer restTemplateCustomizer() { + return new RestTemplateCustomizer() { + @Override + public void customize(RestTemplate restTemplate) { + HttpComponentsClientHttpRequestFactory factory = (HttpComponentsClientHttpRequestFactory) restTemplate + .getRequestFactory(); + factory.setHttpClient(createSslHttpClient()); + } + + private HttpClient createSslHttpClient() { + try { + SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory( + new SSLContextBuilder().loadTrustMaterial(null, + TrustSelfSignedStrategy.INSTANCE).build(), + NoopHostnameVerifier.INSTANCE); + return HttpClients.custom().setSSLSocketFactory(socketFactory) + .build(); + } + catch (Exception ex) { + throw new IllegalStateException("Unable to create SSL HttpClient", + ex); + } + } + }; + } + +} diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockSpring.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockSpring.java index e23dc40879..632ccc7b3d 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockSpring.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockSpring.java @@ -18,13 +18,14 @@ package org.springframework.cloud.contract.wiremock; import javax.net.ssl.HttpsURLConnection; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; + import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.ssl.SSLContexts; import org.junit.Assert; -import org.springframework.util.ClassUtils; -import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import org.springframework.util.ClassUtils; /** * Convenience factory class for a {@link WireMockConfiguration} that knows how to use diff --git a/spring-cloud-contract-wiremock/src/main/resources/META-INF/spring.factories b/spring-cloud-contract-wiremock/src/main/resources/META-INF/spring.factories index 6c450975aa..9ac5aa6677 100644 --- a/spring-cloud-contract-wiremock/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-contract-wiremock/src/main/resources/META-INF/spring.factories @@ -4,4 +4,4 @@ org.springframework.cloud.contract.wiremock.WireMockApplicationListener # RestDocs Auto Configuration org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs=\ -org.springframework.cloud.contract.wiremock.restdocs.WireMockRestDocsConfiguration \ No newline at end of file +org.springframework.cloud.contract.wiremock.restdocs.WireMockRestDocsConfiguration From d3f6ad90a8b907bf7eecf807d6cc58984322f8bd Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 21 Apr 2017 13:26:51 +0100 Subject: [PATCH 04/11] Add a fault injector if server is jetty or tomcat Tomcat sends a slightly different reponse and the client sees it differently in the malformed chunk scenario. It would be good to have the same features for Undertow. See gh-270. --- .../WiremockServerApplicationTests.java | 49 ++++++++-- .../WiremockServerApplicationTests.java | 50 ++++++++-- .../wiremock/SpringBootHttpServerFactory.java | 56 ++++++++--- .../wiremock/TomcatFaultInjector.java | 95 +++++++++++++++++++ .../wiremock/TomcatFaultInjectorFactory.java | 37 ++++++++ .../WiremockServerApplicationTests.java | 15 +-- 6 files changed, 269 insertions(+), 33 deletions(-) create mode 100644 spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/TomcatFaultInjector.java create mode 100644 spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/TomcatFaultInjectorFactory.java diff --git a/samples/wiremock-jetty/src/test/java/com/example/WiremockServerApplicationTests.java b/samples/wiremock-jetty/src/test/java/com/example/WiremockServerApplicationTests.java index 6c6f1e6da9..9bbb1671d6 100644 --- a/samples/wiremock-jetty/src/test/java/com/example/WiremockServerApplicationTests.java +++ b/samples/wiremock-jetty/src/test/java/com/example/WiremockServerApplicationTests.java @@ -1,14 +1,17 @@ package com.example; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static org.assertj.core.api.Assertions.assertThat; +import com.github.tomakehurst.wiremock.http.Fault; +import com.github.tomakehurst.wiremock.junit.WireMockClassRule; +import org.apache.http.MalformedChunkCodingException; +import org.apache.http.NoHttpResponseException; +import org.apache.http.client.ClientProtocolException; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; @@ -16,7 +19,12 @@ import org.springframework.cloud.contract.wiremock.WireMockSpring; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; -import com.github.tomakehurst.wiremock.junit.WireMockClassRule; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.CoreMatchers.instanceOf; @RunWith(SpringRunner.class) @SpringBootTest(properties="app.baseUrl=http://localhost:6061", webEnvironment=WebEnvironment.NONE) @@ -26,14 +34,41 @@ public class WiremockServerApplicationTests { @ClassRule public static WireMockClassRule wiremock = new WireMockClassRule(WireMockSpring.options().port(6061)); + @Rule + public ExpectedException expected = ExpectedException.none(); + @Autowired private Service service; @Test - public void contextLoads() throws Exception { + public void hello() throws Exception { stubFor(get(urlEqualTo("/resource")) .willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!"))); assertThat(this.service.go()).isEqualTo("Hello World!"); } + @Test + public void randomData() throws Exception { + stubFor(get(urlEqualTo("/resource")) + .willReturn(aResponse().withFault(Fault.RANDOM_DATA_THEN_CLOSE))); + expected.expectCause(instanceOf(ClientProtocolException.class)); + assertThat(this.service.go()).isEqualTo("Oops!"); + } + + @Test + public void emptyResponse() throws Exception { + stubFor(get(urlEqualTo("/resource")) + .willReturn(aResponse().withFault(Fault.EMPTY_RESPONSE))); + expected.expectCause(instanceOf(NoHttpResponseException.class)); + assertThat(this.service.go()).isEqualTo("Oops!"); + } + + @Test + public void malformed() throws Exception { + stubFor(get(urlEqualTo("/resource")) + .willReturn(aResponse().withFault(Fault.MALFORMED_RESPONSE_CHUNK))); + expected.expectCause(instanceOf(MalformedChunkCodingException.class)); + assertThat(this.service.go()).isEqualTo("Oops!"); + } + } diff --git a/samples/wiremock-tomcat/src/test/java/com/example/WiremockServerApplicationTests.java b/samples/wiremock-tomcat/src/test/java/com/example/WiremockServerApplicationTests.java index d7f2980b6f..d4c5656527 100644 --- a/samples/wiremock-tomcat/src/test/java/com/example/WiremockServerApplicationTests.java +++ b/samples/wiremock-tomcat/src/test/java/com/example/WiremockServerApplicationTests.java @@ -1,14 +1,18 @@ package com.example; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; +import com.github.tomakehurst.wiremock.http.Fault; +import com.github.tomakehurst.wiremock.junit.WireMockClassRule; + +import org.apache.http.NoHttpResponseException; +import org.apache.http.client.ClientProtocolException; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; @@ -16,7 +20,12 @@ import org.springframework.cloud.contract.wiremock.WireMockSpring; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; -import com.github.tomakehurst.wiremock.junit.WireMockClassRule; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.CoreMatchers.instanceOf; @RunWith(SpringRunner.class) @SpringBootTest(properties="app.baseUrl=http://localhost:6067", webEnvironment=WebEnvironment.NONE) @@ -26,6 +35,9 @@ public class WiremockServerApplicationTests { @ClassRule public static WireMockClassRule wiremock = new WireMockClassRule(WireMockSpring.options().port(6067)); + @Rule + public ExpectedException expected = ExpectedException.none(); + @Autowired private Service service; @@ -36,4 +48,30 @@ public class WiremockServerApplicationTests { assertThat(this.service.go()).isEqualTo("Hello World!"); } + @Test + public void randomData() throws Exception { + stubFor(get(urlEqualTo("/resource")) + .willReturn(aResponse().withFault(Fault.RANDOM_DATA_THEN_CLOSE))); + expected.expectCause(instanceOf(ClientProtocolException.class)); + assertThat(this.service.go()).isEqualTo("Oops!"); + } + + @Test + public void emptyResponse() throws Exception { + stubFor(get(urlEqualTo("/resource")) + .willReturn(aResponse().withFault(Fault.EMPTY_RESPONSE))); + expected.expectCause(instanceOf(NoHttpResponseException.class)); + assertThat(this.service.go()).isEqualTo("Oops!"); + } + + @Test + public void malformed() throws Exception { + stubFor(get(urlEqualTo("/resource")) + .willReturn(aResponse().withFault(Fault.MALFORMED_RESPONSE_CHUNK))); + // It's a different exception type than Jetty, but it's in the right ballpark + expected.expectCause(instanceOf(IOException.class)); + expected.expectMessage("chunk"); + assertThat(this.service.go()).isEqualTo("Oops!"); + } + } diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/SpringBootHttpServerFactory.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/SpringBootHttpServerFactory.java index ecf36be8cb..3c4fc2f84c 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/SpringBootHttpServerFactory.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/SpringBootHttpServerFactory.java @@ -18,11 +18,28 @@ package org.springframework.cloud.contract.wiremock; import javax.servlet.ServletContext; +import io.undertow.Undertow.Builder; + +import com.github.tomakehurst.wiremock.common.HttpsSettings; +import com.github.tomakehurst.wiremock.common.Notifier; +import com.github.tomakehurst.wiremock.core.Options; +import com.github.tomakehurst.wiremock.core.WireMockApp; +import com.github.tomakehurst.wiremock.http.AdminRequestHandler; +import com.github.tomakehurst.wiremock.http.HttpServer; +import com.github.tomakehurst.wiremock.http.HttpServerFactory; +import com.github.tomakehurst.wiremock.http.RequestHandler; +import com.github.tomakehurst.wiremock.http.StubRequestHandler; +import com.github.tomakehurst.wiremock.jetty9.JettyFaultInjectorFactory; +import com.github.tomakehurst.wiremock.servlet.FaultInjectorFactory; +import com.github.tomakehurst.wiremock.servlet.NoFaultInjectorFactory; +import com.github.tomakehurst.wiremock.servlet.WireMockHandlerDispatchingServlet; + import org.apache.catalina.connector.Connector; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; @@ -62,21 +79,9 @@ import org.springframework.context.annotation.Import; import org.springframework.context.event.EventListener; import org.springframework.context.support.GenericApplicationContext; import org.springframework.stereotype.Component; +import org.springframework.util.ClassUtils; import org.springframework.web.context.ServletContextAware; -import com.github.tomakehurst.wiremock.common.HttpsSettings; -import com.github.tomakehurst.wiremock.common.Notifier; -import com.github.tomakehurst.wiremock.core.Options; -import com.github.tomakehurst.wiremock.core.WireMockApp; -import com.github.tomakehurst.wiremock.http.AdminRequestHandler; -import com.github.tomakehurst.wiremock.http.HttpServer; -import com.github.tomakehurst.wiremock.http.HttpServerFactory; -import com.github.tomakehurst.wiremock.http.RequestHandler; -import com.github.tomakehurst.wiremock.http.StubRequestHandler; -import com.github.tomakehurst.wiremock.servlet.WireMockHandlerDispatchingServlet; - -import io.undertow.Undertow.Builder; - /** * @author Dave Syer * @@ -258,6 +263,8 @@ class WiremockServerConfiguration { @Autowired private StubRequestHandler stubRequestHandler; @Autowired + private FaultInjectorFactory faultInjectorFactory; + @Autowired private Options options; @Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) @@ -265,6 +272,10 @@ class WiremockServerConfiguration { ServletRegistrationBean reg = new ServletRegistrationBean(); reg.addInitParameter(RequestHandler.HANDLER_CLASS_KEY, StubRequestHandler.class.getName()); + if (WiremockServerConfiguration.this.faultInjectorFactory != null) { + reg.addInitParameter(FaultInjectorFactory.INJECTOR_CLASS_KEY, + FaultInjectorFactory.class.getName()); + } reg.setServlet(new WireMockHandlerDispatchingServlet()); reg.setName("stub"); reg.addUrlMappings("/"); @@ -293,6 +304,10 @@ class WiremockServerConfiguration { WiremockServerConfiguration.this.stubRequestHandler); servletContext.setAttribute(Notifier.KEY, WiremockServerConfiguration.this.options.notifier()); + if (WiremockServerConfiguration.this.faultInjectorFactory != null) { + servletContext.setAttribute(FaultInjectorFactory.class.getName(), + WiremockServerConfiguration.this.faultInjectorFactory); + } } }; } @@ -367,6 +382,11 @@ class ContainerConfiguration { return tomcat; } + @Bean + public FaultInjectorFactory faultInjectorFactory() { + return new TomcatFaultInjectorFactory(); + } + @EventListener public void serverUp(EmbeddedServletContainerInitializedEvent event) { if (this.connector != null) { @@ -417,6 +437,11 @@ class ContainerConfiguration { return undertow; } + @Bean + public FaultInjectorFactory faultInjectorFactory() { + return new NoFaultInjectorFactory(); + } + @EventListener public void serverUp(EmbeddedServletContainerInitializedEvent event) { if (this.port != null) { @@ -457,6 +482,11 @@ class ContainerConfiguration { return jetty; } + @Bean + public JettyFaultInjectorFactory faultInjectorFactory() { + return new JettyFaultInjectorFactory(); + } + private org.eclipse.jetty.server.Connector createStandardConnector( Server server) { ServerConnector connector = new ServerConnector(server, -1, -1); diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/TomcatFaultInjector.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/TomcatFaultInjector.java new file mode 100644 index 0000000000..e7b4000873 --- /dev/null +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/TomcatFaultInjector.java @@ -0,0 +1,95 @@ +/* + * Copyright 2016-2017 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 + * + * 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 org.springframework.cloud.contract.wiremock; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.nio.ByteBuffer; + +import javax.servlet.http.HttpServletResponse; + +import com.github.tomakehurst.wiremock.common.Exceptions; +import com.github.tomakehurst.wiremock.core.FaultInjector; +import com.google.common.base.Charsets; + +import org.apache.coyote.Response; +import org.apache.tomcat.util.net.SocketWrapperBase; + +import org.springframework.util.ReflectionUtils; + +import static com.github.tomakehurst.wiremock.common.Exceptions.throwUnchecked; + +/** + * @author Dave Syer + * + */ +public class TomcatFaultInjector implements FaultInjector { + + private static final byte[] GARBAGE = "lskdu018973t09sylgasjkfg1][]'./.sdlv" + .getBytes(Charsets.UTF_8); + private final Response response; + private SocketWrapperBase socket; + + public TomcatFaultInjector(HttpServletResponse response) { + this.response = ((org.apache.catalina.connector.Response) getField(response, + "response")).getCoyoteResponse(); + this.socket = (SocketWrapperBase) getField( + getField(this.response, "outputBuffer"), "socketWrapper"); + } + + private Object getField(Object target, String string) { + Field field = ReflectionUtils.findField(target.getClass(), string); + ReflectionUtils.makeAccessible(field); + return ReflectionUtils.getField(field, target); + } + + @Override + public void emptyResponseAndCloseConnection() { + try { + socket.close(); + } + catch (IOException e) { + Exceptions.throwUnchecked(e); + } + } + + @Override + public void malformedResponseChunk() { + try { + response.sendHeaders(); + response.doWrite(ByteBuffer.wrap(GARBAGE)); + socket.flush(true); + socket.close(); + } + catch (IOException e) { + throwUnchecked(e); + } + } + + @Override + public void randomDataAndCloseConnection() { + try { + socket.write(true, GARBAGE, 0, GARBAGE.length); + socket.flush(true); + socket.close(); + } + catch (IOException e) { + throwUnchecked(e); + } + } + +} diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/TomcatFaultInjectorFactory.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/TomcatFaultInjectorFactory.java new file mode 100644 index 0000000000..17cbcaea2d --- /dev/null +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/TomcatFaultInjectorFactory.java @@ -0,0 +1,37 @@ +/* + * Copyright 2016-2017 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 + * + * 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 org.springframework.cloud.contract.wiremock; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.github.tomakehurst.wiremock.core.FaultInjector; +import com.github.tomakehurst.wiremock.servlet.FaultInjectorFactory; + +/** + * @author Dave Syer + * + */ +public class TomcatFaultInjectorFactory implements FaultInjectorFactory { + + @Override + public FaultInjector buildFaultInjector(HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse) { + return new TomcatFaultInjector(httpServletResponse); + } + +} diff --git a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/WiremockServerApplicationTests.java b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/WiremockServerApplicationTests.java index 34d7f57f9e..dd6a002d33 100644 --- a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/WiremockServerApplicationTests.java +++ b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/WiremockServerApplicationTests.java @@ -1,21 +1,22 @@ package org.springframework.cloud.contract.wiremock; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static org.assertj.core.api.Assertions.assertThat; +import com.github.tomakehurst.wiremock.junit.WireMockClassRule; import org.junit.ClassRule; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; -import com.github.tomakehurst.wiremock.junit.WireMockClassRule; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest(classes=WiremockTestsApplication.class, properties="app.baseUrl=http://localhost:8080", webEnvironment=WebEnvironment.NONE) @@ -29,7 +30,7 @@ public class WiremockServerApplicationTests { private Service service; @Test - public void contextLoads() throws Exception { + public void hello() throws Exception { stubFor(get(urlEqualTo("/test")) .willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!"))); assertThat(this.service.go()).isEqualTo("Hello World!"); From b98223b08230b67f266562f3fcde5c802d20cf9c Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Mon, 24 Apr 2017 15:02:50 +0200 Subject: [PATCH 05/11] Fixed checkstyle --- .../contract/wiremock/TomcatFaultInjector.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/TomcatFaultInjector.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/TomcatFaultInjector.java index e7b4000873..0abf792f85 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/TomcatFaultInjector.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/TomcatFaultInjector.java @@ -60,7 +60,7 @@ public class TomcatFaultInjector implements FaultInjector { @Override public void emptyResponseAndCloseConnection() { try { - socket.close(); + this.socket.close(); } catch (IOException e) { Exceptions.throwUnchecked(e); @@ -70,10 +70,10 @@ public class TomcatFaultInjector implements FaultInjector { @Override public void malformedResponseChunk() { try { - response.sendHeaders(); - response.doWrite(ByteBuffer.wrap(GARBAGE)); - socket.flush(true); - socket.close(); + this.response.sendHeaders(); + this.response.doWrite(ByteBuffer.wrap(GARBAGE)); + this.socket.flush(true); + this.socket.close(); } catch (IOException e) { throwUnchecked(e); @@ -83,9 +83,9 @@ public class TomcatFaultInjector implements FaultInjector { @Override public void randomDataAndCloseConnection() { try { - socket.write(true, GARBAGE, 0, GARBAGE.length); - socket.flush(true); - socket.close(); + this.socket.write(true, GARBAGE, 0, GARBAGE.length); + this.socket.flush(true); + this.socket.close(); } catch (IOException e) { throwUnchecked(e); From e9770c6ac2d2450964a7261dbc4a795df56b335b Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Mon, 24 Apr 2017 15:05:43 +0200 Subject: [PATCH 06/11] Fixed the build --- pom.xml | 2 +- samples/standalone/dsl/http-server/pom.xml | 2 +- samples/standalone/dsl/pom.xml | 2 +- samples/standalone/messaging/pom.xml | 2 +- samples/standalone/messaging/stream-source/pom.xml | 2 +- samples/standalone/pact/pact-http-server/pom.xml | 2 +- samples/standalone/pact/pom.xml | 2 +- samples/standalone/pom.xml | 2 +- samples/standalone/restdocs/pom.xml | 2 +- .../spring-cloud-contract-gradle-plugin/build.gradle | 2 +- .../spring-cloud-contract-gradle-plugin/gradle.properties | 2 +- .../test/resources/functionalTest/bootSimple/gradle.properties | 2 +- .../functionalTest/sampleJerseyProject/gradle.properties | 2 +- .../resources/functionalTest/sampleProject/gradle.properties | 2 +- .../resources/functionalTest/scenarioProject/gradle.properties | 2 +- .../src/test/projects/complex-remote-contracts/pom.xml | 2 +- .../src/test/projects/pact/pom.xml | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index 67b3b4604e..55a620512f 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ 1.5.3.BUILD-SNAPSHOT 2.17 1.3.1.BUILD-SNAPSHOT - 1.1.0.BUILD-SNAPSHOT + 1.1.1.BUILD-SNAPSHOT Chelsea.BUILD-SNAPSHOT 1.3.0.BUILD-SNAPSHOT 1.2.0.BUILD-SNAPSHOT diff --git a/samples/standalone/dsl/http-server/pom.xml b/samples/standalone/dsl/http-server/pom.xml index e0a6490c58..772176997a 100644 --- a/samples/standalone/dsl/http-server/pom.xml +++ b/samples/standalone/dsl/http-server/pom.xml @@ -117,7 +117,7 @@ org.springframework.cloud spring-cloud-contract-maven-plugin - [1.1.0.BUILD-SNAPSHOT,) + [1.1.1.BUILD-SNAPSHOT,) convert generateTests diff --git a/samples/standalone/dsl/pom.xml b/samples/standalone/dsl/pom.xml index 13c73b511d..1246a5d6d3 100644 --- a/samples/standalone/dsl/pom.xml +++ b/samples/standalone/dsl/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-samples-standalone - 1.1.0.BUILD-SNAPSHOT + 1.1.1.BUILD-SNAPSHOT .. diff --git a/samples/standalone/messaging/pom.xml b/samples/standalone/messaging/pom.xml index 8893959be8..05616bbc2c 100644 --- a/samples/standalone/messaging/pom.xml +++ b/samples/standalone/messaging/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-samples-standalone - 1.1.0.BUILD-SNAPSHOT + 1.1.1.BUILD-SNAPSHOT .. diff --git a/samples/standalone/messaging/stream-source/pom.xml b/samples/standalone/messaging/stream-source/pom.xml index f11d74d600..e11c1616e0 100644 --- a/samples/standalone/messaging/stream-source/pom.xml +++ b/samples/standalone/messaging/stream-source/pom.xml @@ -156,7 +156,7 @@ org.springframework.cloud spring-cloud-contract-maven-plugin - [1.1.0.BUILD-SNAPSHOT,) + [1.1.1.BUILD-SNAPSHOT,) convert generateTests diff --git a/samples/standalone/pact/pact-http-server/pom.xml b/samples/standalone/pact/pact-http-server/pom.xml index 310125e090..13eb8c236a 100644 --- a/samples/standalone/pact/pact-http-server/pom.xml +++ b/samples/standalone/pact/pact-http-server/pom.xml @@ -125,7 +125,7 @@ org.springframework.cloud spring-cloud-contract-maven-plugin - [1.1.0.BUILD-SNAPSHOT,) + [1.1.1.BUILD-SNAPSHOT,) convert generateTests diff --git a/samples/standalone/pact/pom.xml b/samples/standalone/pact/pom.xml index 3b582cc2b7..56469b3a2b 100644 --- a/samples/standalone/pact/pom.xml +++ b/samples/standalone/pact/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-samples-standalone - 1.1.0.BUILD-SNAPSHOT + 1.1.1.BUILD-SNAPSHOT .. diff --git a/samples/standalone/pom.xml b/samples/standalone/pom.xml index 56f1211331..85fb0941cc 100644 --- a/samples/standalone/pom.xml +++ b/samples/standalone/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-samples - 1.1.0.BUILD-SNAPSHOT + 1.1.1.BUILD-SNAPSHOT .. diff --git a/samples/standalone/restdocs/pom.xml b/samples/standalone/restdocs/pom.xml index 45d64b19f8..bf1aad247f 100644 --- a/samples/standalone/restdocs/pom.xml +++ b/samples/standalone/restdocs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-samples-standalone - 1.1.0.BUILD-SNAPSHOT + 1.1.1.BUILD-SNAPSHOT .. diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle index 1c4613a77b..dc8a6bca6f 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle @@ -23,7 +23,7 @@ ext { ] } -project.version = findProperty('verifierVersion') ?: '1.1.0.BUILD-SNAPSHOT' +project.version = findProperty('verifierVersion') ?: '1.1.1.BUILD-SNAPSHOT' apply plugin: 'groovy' apply from: "$rootDir/gradle/release.gradle" apply plugin: 'eclipse' diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties index dc488d8fa7..1c3fb93350 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties @@ -16,5 +16,5 @@ nexusUsername = nexusPassword = -verifierVersion=1.1.0.BUILD-SNAPSHOT +verifierVersion=1.1.1.BUILD-SNAPSHOT org.gradle.daemon=false \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties index 5b12dfc27a..ef921a31eb 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties @@ -15,5 +15,5 @@ # wiremockVersion=2.5.1 jsonAssertVersion=0.4.8 -verifierVersion=1.1.0.BUILD-SNAPSHOT +verifierVersion=1.1.1.BUILD-SNAPSHOT diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties index 71b597b876..6f9a459444 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties @@ -15,4 +15,4 @@ # wiremockVersion=2.5.1 jsonAssertVersion=0.4.8 -verifierVersion=1.1.0.BUILD-SNAPSHOT +verifierVersion=1.1.1.BUILD-SNAPSHOT diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties index c6339b65ea..33ab5a5b1c 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties @@ -15,4 +15,4 @@ # wiremockVersion=2.5.1 jsonAssertVersion=0.4.8 -verifierVersion=1.1.0.BUILD-SNAPSHOT \ No newline at end of file +verifierVersion=1.1.1.BUILD-SNAPSHOT \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties index 5b12dfc27a..ef921a31eb 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties @@ -15,5 +15,5 @@ # wiremockVersion=2.5.1 jsonAssertVersion=0.4.8 -verifierVersion=1.1.0.BUILD-SNAPSHOT +verifierVersion=1.1.1.BUILD-SNAPSHOT diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-remote-contracts/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-remote-contracts/pom.xml index 5f68ede9cf..50f3621a39 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-remote-contracts/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-remote-contracts/pom.xml @@ -30,7 +30,7 @@ org.springframework.cloud spring-cloud-contract-maven-plugin - 1.1.0.BUILD-SNAPSHOT + 1.1.1.BUILD-SNAPSHOT com/example/server diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/pact/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/pact/pom.xml index 297e4e005e..b4e5c89df7 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/pact/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/pact/pom.xml @@ -26,7 +26,7 @@ 0.1 - 1.1.0.BUILD-SNAPSHOT + 1.1.1.BUILD-SNAPSHOT From 1f71cde2be63ef7f9102e37d5aa6e212d5328fbb Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Mon, 24 Apr 2017 15:06:52 +0200 Subject: [PATCH 07/11] Removed unused import --- .../cloud/contract/wiremock/SpringBootHttpServerFactory.java | 1 - 1 file changed, 1 deletion(-) diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/SpringBootHttpServerFactory.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/SpringBootHttpServerFactory.java index 3c4fc2f84c..cb68825dc5 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/SpringBootHttpServerFactory.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/SpringBootHttpServerFactory.java @@ -79,7 +79,6 @@ import org.springframework.context.annotation.Import; import org.springframework.context.event.EventListener; import org.springframework.context.support.GenericApplicationContext; import org.springframework.stereotype.Component; -import org.springframework.util.ClassUtils; import org.springframework.web.context.ServletContextAware; /** From 5b8256d7293167ed55fa208bf7904e571a111579 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Mon, 24 Apr 2017 15:38:57 +0200 Subject: [PATCH 08/11] Bumping versions --- pom.xml | 10 +++++----- spring-cloud-contract-dependencies/pom.xml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 55a620512f..f5165f2022 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-build - 1.3.1.BUILD-SNAPSHOT + 1.3.2.BUILD-SNAPSHOT @@ -27,12 +27,12 @@ 2.17.0 1.5.3.BUILD-SNAPSHOT 2.17 - 1.3.1.BUILD-SNAPSHOT + 1.3.2.BUILD-SNAPSHOT 1.1.1.BUILD-SNAPSHOT Chelsea.BUILD-SNAPSHOT - 1.3.0.BUILD-SNAPSHOT - 1.2.0.BUILD-SNAPSHOT - 1.2.0.BUILD-SNAPSHOT + 1.3.1.BUILD-SNAPSHOT + 1.2.1.BUILD-SNAPSHOT + 1.2.1.BUILD-SNAPSHOT diff --git a/spring-cloud-contract-dependencies/pom.xml b/spring-cloud-contract-dependencies/pom.xml index ea304b54f1..393d99b475 100644 --- a/spring-cloud-contract-dependencies/pom.xml +++ b/spring-cloud-contract-dependencies/pom.xml @@ -5,7 +5,7 @@ spring-cloud-dependencies-parent org.springframework.cloud - 1.3.1.BUILD-SNAPSHOT + 1.3.2.BUILD-SNAPSHOT spring-cloud-contract-dependencies From cf637d040f3d724f64b2c12e9281670a54972906 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 24 Apr 2017 14:45:42 +0100 Subject: [PATCH 09/11] Add custom jetty fault injector for 9.4 --- .../contract/wiremock/JettyFaultInjector.java | 93 +++++++++++++++++++ .../wiremock/JettyFaultInjectorFactory.java | 37 ++++++++ .../wiremock/SpringBootHttpServerFactory.java | 1 - 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/JettyFaultInjector.java create mode 100644 spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/JettyFaultInjectorFactory.java diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/JettyFaultInjector.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/JettyFaultInjector.java new file mode 100644 index 0000000000..ab3e2822b8 --- /dev/null +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/JettyFaultInjector.java @@ -0,0 +1,93 @@ +/* + * Copyright 2016-2017 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 + * + * 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 org.springframework.cloud.contract.wiremock; + +import java.io.IOException; +import java.nio.channels.ByteChannel; + +import javax.servlet.http.HttpServletResponse; + +import com.github.tomakehurst.wiremock.core.FaultInjector; +import com.google.common.base.Charsets; + +import org.eclipse.jetty.io.ChannelEndPoint; +import org.eclipse.jetty.server.HttpChannel; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.BufferUtil; + +import static com.github.tomakehurst.wiremock.common.Exceptions.throwUnchecked; +import static com.github.tomakehurst.wiremock.jetty9.JettyUtils.unwrapResponse; + +/** + * @author Dave Syer + * + */ +public class JettyFaultInjector implements FaultInjector { + + private static final byte[] GARBAGE = "lskdu018973t09sylgasjkfg1][]'./.sdlv" + .getBytes(Charsets.UTF_8); + + private final Response response; + private final ByteChannel socket; + + public JettyFaultInjector(HttpServletResponse response) { + this.response = unwrapResponse(response); + this.socket = socket(); + } + + @Override + public void emptyResponseAndCloseConnection() { + try { + this.socket.close(); + } + catch (IOException e) { + throwUnchecked(e); + } + } + + @Override + public void malformedResponseChunk() { + try { + this.response.setStatus(200); + this.response.flushBuffer(); + this.socket.write(BufferUtil.toBuffer(GARBAGE)); + this.socket.close(); + } + catch (IOException e) { + throwUnchecked(e); + } + + } + + @Override + public void randomDataAndCloseConnection() { + try { + this.socket.write(BufferUtil.toBuffer(GARBAGE)); + this.socket.close(); + } + catch (IOException e) { + throwUnchecked(e); + } + } + + private ByteChannel socket() { + HttpChannel httpChannel = this.response.getHttpOutput().getHttpChannel(); + ChannelEndPoint ep = (ChannelEndPoint) httpChannel.getEndPoint(); + return ep.getChannel(); + } + +} diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/JettyFaultInjectorFactory.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/JettyFaultInjectorFactory.java new file mode 100644 index 0000000000..00ebaa6c3b --- /dev/null +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/JettyFaultInjectorFactory.java @@ -0,0 +1,37 @@ +/* + * Copyright 2016-2017 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 + * + * 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 org.springframework.cloud.contract.wiremock; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.github.tomakehurst.wiremock.core.FaultInjector; +import com.github.tomakehurst.wiremock.servlet.FaultInjectorFactory; + +/** + * @author Dave Syer + * + */ +public class JettyFaultInjectorFactory implements FaultInjectorFactory { + + @Override + public FaultInjector buildFaultInjector(HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse) { + return new JettyFaultInjector(httpServletResponse); + } + +} diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/SpringBootHttpServerFactory.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/SpringBootHttpServerFactory.java index cb68825dc5..03d655b972 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/SpringBootHttpServerFactory.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/SpringBootHttpServerFactory.java @@ -29,7 +29,6 @@ import com.github.tomakehurst.wiremock.http.HttpServer; import com.github.tomakehurst.wiremock.http.HttpServerFactory; import com.github.tomakehurst.wiremock.http.RequestHandler; import com.github.tomakehurst.wiremock.http.StubRequestHandler; -import com.github.tomakehurst.wiremock.jetty9.JettyFaultInjectorFactory; import com.github.tomakehurst.wiremock.servlet.FaultInjectorFactory; import com.github.tomakehurst.wiremock.servlet.NoFaultInjectorFactory; import com.github.tomakehurst.wiremock.servlet.WireMockHandlerDispatchingServlet; From 4794354dd68fef4d05451696f8a6a7bde61d8a9f Mon Sep 17 00:00:00 2001 From: Ruslan Stelmachenko Date: Tue, 25 Apr 2017 08:40:38 +0300 Subject: [PATCH 10/11] Fix typo, add links to MockMvc and RestAssured docs (#274) The `RestAssured` framework was mistakenly named `RestEasy` in the documentation. --- README.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index 137d210f42..fb46ea0f0a 100644 --- a/README.adoc +++ b/README.adoc @@ -198,7 +198,8 @@ Spring Boot container if there is one. https://projects.spring.io/spring-restdocs[Spring RestDocs] can be used to generate documentation (e.g. in asciidoctor format) for an -HTTP API with Spring MockMvc or RestEasy. At the same time as you +HTTP API with https://docs.spring.io/spring/docs/current/spring-framework-reference/html/integration-testing.html#spring-mvc-test-framework[Spring MockMvc] +or http://rest-assured.io/[RestAssured]. At the same time as you generate documentation for your API, you can also generate WireMock stubs, by using Spring Cloud Contract WireMock. Just write your normal RestDocs test cases and use `@AutoConfigureRestDocs` to have stubs From fdd457e91289df089222fbb37d58810b94308a59 Mon Sep 17 00:00:00 2001 From: Ruslan Stelmachenko Date: Tue, 25 Apr 2017 08:40:38 +0300 Subject: [PATCH 11/11] Fix typo, add links to MockMvc and RestAssured docs (#274) The `RestAssured` framework was mistakenly named `RestEasy` in the documentation. --- README.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index e502cfc6ef..f4f2837e96 100644 --- a/README.adoc +++ b/README.adoc @@ -198,7 +198,8 @@ Spring Boot container if there is one. https://projects.spring.io/spring-restdocs[Spring RestDocs] can be used to generate documentation (e.g. in asciidoctor format) for an -HTTP API with Spring MockMvc or RestEasy. At the same time as you +HTTP API with https://docs.spring.io/spring/docs/current/spring-framework-reference/html/integration-testing.html#spring-mvc-test-framework[Spring MockMvc] +or http://rest-assured.io/[RestAssured]. At the same time as you generate documentation for your API, you can also generate WireMock stubs, by using Spring Cloud Contract WireMock. Just write your normal RestDocs test cases and use `@AutoConfigureRestDocs` to have stubs