From 96a294828b4ed94df1171cd212e3f89027294ca2 Mon Sep 17 00:00:00 2001 From: Piotr Fus Date: Mon, 29 Jan 2018 18:36:48 +0100 Subject: [PATCH 1/7] Fix some typos Change-Id: Ia877cc10f76e27c1d12ffdbbff399dd89e25f319 --- .../mockserver/server/ContextExecutor.groovy | 8 ++++---- .../mockserver/server/HttpMockServer.groovy | 20 +++++++++---------- ...Wraper.groovy => HttpServerWrapper.groovy} | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) rename mockserver/src/main/groovy/pl/touk/mockserver/server/{HttpServerWraper.groovy => HttpServerWrapper.groovy} (95%) diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/ContextExecutor.groovy b/mockserver/src/main/groovy/pl/touk/mockserver/server/ContextExecutor.groovy index b26eb5a..ce11c4c 100644 --- a/mockserver/src/main/groovy/pl/touk/mockserver/server/ContextExecutor.groovy +++ b/mockserver/src/main/groovy/pl/touk/mockserver/server/ContextExecutor.groovy @@ -10,15 +10,15 @@ import java.util.concurrent.CopyOnWriteArrayList @Slf4j @PackageScope class ContextExecutor { - private final HttpServerWraper httpServerWraper + private final HttpServerWrapper httpServerWrapper final String path private final List mocks - ContextExecutor(HttpServerWraper httpServerWraper, Mock initialMock) { - this.httpServerWraper = httpServerWraper + ContextExecutor(HttpServerWrapper httpServerWrapper, Mock initialMock) { + this.httpServerWrapper = httpServerWrapper this.path = "/${initialMock.path}" this.mocks = new CopyOnWriteArrayList<>([initialMock]) - httpServerWraper.createContext(path) { + httpServerWrapper.createContext(path) { HttpExchange ex -> try { applyMocks(ex) diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy index 7d2f86b..082593a 100644 --- a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy +++ b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy @@ -30,8 +30,8 @@ import static pl.touk.mockserver.server.Util.createResponse @Slf4j class HttpMockServer { - private final HttpServerWraper httpServerWraper - private final Map childServers = new ConcurrentHashMap<>() + private final HttpServerWrapper httpServerWrapper + private final Map childServers = new ConcurrentHashMap<>() private final Set mockNames = new CopyOnWriteArraySet<>() private final ConfigObject configuration = new ConfigObject() private final Executor executor @@ -41,13 +41,13 @@ class HttpMockServer { HttpMockServer(int port = 9999, ConfigObject initialConfiguration = new ConfigObject(), int threads = 10) { executor = Executors.newFixedThreadPool(threads) - httpServerWraper = new HttpServerWraper(port, executor) + httpServerWrapper = new HttpServerWrapper(port, executor) initialConfiguration.values()?.each { ConfigObject co -> addMock(co) } - httpServerWraper.createContext('/serverControl', { + httpServerWrapper.createContext('/serverControl', { HttpExchange ex -> try { if (ex.requestMethod == 'GET') { @@ -108,7 +108,7 @@ class HttpMockServer { throw new RuntimeException('mock already registered') } Mock mock = mockFromRequest(request) - HttpServerWraper child = getOrCreateChildServer(mock.port) + HttpServerWrapper child = getOrCreateChildServer(mock.port) child.addMock(mock) saveConfiguration(request) mockNames << name @@ -121,7 +121,7 @@ class HttpMockServer { throw new RuntimeException('mock already registered') } Mock mock = mockFromConfig(co) - HttpServerWraper child = getOrCreateChildServer(mock.port) + HttpServerWrapper child = getOrCreateChildServer(mock.port) child.addMock(mock) configuration.put(name, co) mockNames << name @@ -173,10 +173,10 @@ class HttpMockServer { return mock } - private HttpServerWraper getOrCreateChildServer(int mockPort) { - HttpServerWraper child = childServers[mockPort] + private HttpServerWrapper getOrCreateChildServer(int mockPort) { + HttpServerWrapper child = childServers[mockPort] if (!child) { - child = new HttpServerWraper(mockPort, executor) + child = new HttpServerWrapper(mockPort, executor) childServers.put(mockPort, child) } return child @@ -244,6 +244,6 @@ class HttpMockServer { void stop() { childServers.values().each { it.stop() } - httpServerWraper.stop() + httpServerWrapper.stop() } } diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWraper.groovy b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy similarity index 95% rename from mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWraper.groovy rename to mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy index e1ef2f8..6f8f9f4 100644 --- a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWraper.groovy +++ b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy @@ -9,13 +9,13 @@ import java.util.concurrent.Executor @Slf4j @PackageScope -class HttpServerWraper { +class HttpServerWrapper { private final HttpServer httpServer final int port private List executors = [] - HttpServerWraper(int port, Executor executor) { + HttpServerWrapper(int port, Executor executor) { this.port = port InetSocketAddress addr = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), port) httpServer = HttpServer.create(addr, 0) From 0323749ff429d80bd364648c829cc10a8b175294 Mon Sep 17 00:00:00 2001 From: Piotr Fus Date: Mon, 29 Jan 2018 20:26:55 +0100 Subject: [PATCH 2/7] Add https publishing Change-Id: I7611b7379c0f42d02342adc1eec56c0d79caa432 --- .../xsd/pl/touk/mockserver/api/common.xsd | 8 ++ .../xsd/pl/touk/mockserver/api/request.xsd | 1 + .../tests/MockServerHttpsTest.groovy | 80 ++++++++++++++++++ .../src/test/resources/keystore.jks | Bin 0 -> 2257 bytes .../mockserver/server/HttpMockServer.groovy | 12 +-- .../server/HttpServerWrapper.groovy | 33 +++++++- .../pl/touk/mockserver/server/Mock.groovy | 2 + 7 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerHttpsTest.groovy create mode 100644 mockserver-tests/src/test/resources/keystore.jks diff --git a/mockserver-api/src/main/xsd/pl/touk/mockserver/api/common.xsd b/mockserver-api/src/main/xsd/pl/touk/mockserver/api/common.xsd index ac21246..953b839 100644 --- a/mockserver-api/src/main/xsd/pl/touk/mockserver/api/common.xsd +++ b/mockserver-api/src/main/xsd/pl/touk/mockserver/api/common.xsd @@ -18,5 +18,13 @@ + + + + + + + + diff --git a/mockserver-api/src/main/xsd/pl/touk/mockserver/api/request.xsd b/mockserver-api/src/main/xsd/pl/touk/mockserver/api/request.xsd index 6636b53..23786bc 100644 --- a/mockserver-api/src/main/xsd/pl/touk/mockserver/api/request.xsd +++ b/mockserver-api/src/main/xsd/pl/touk/mockserver/api/request.xsd @@ -20,6 +20,7 @@ + diff --git a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerHttpsTest.groovy b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerHttpsTest.groovy new file mode 100644 index 0000000..91b851a --- /dev/null +++ b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerHttpsTest.groovy @@ -0,0 +1,80 @@ +package pl.touk.mockserver.tests + +import groovy.util.slurpersupport.GPathResult +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpPost +import org.apache.http.conn.ssl.SSLConnectionSocketFactory +import org.apache.http.conn.ssl.SSLContexts +import org.apache.http.entity.ContentType +import org.apache.http.entity.StringEntity +import org.apache.http.impl.client.CloseableHttpClient +import org.apache.http.impl.client.HttpClients +import pl.touk.mockserver.api.common.Https +import pl.touk.mockserver.api.request.AddMock +import pl.touk.mockserver.client.RemoteMockServer +import pl.touk.mockserver.client.Util +import pl.touk.mockserver.server.HttpMockServer +import spock.lang.Shared +import spock.lang.Specification + +import javax.net.ssl.SSLContext +import java.security.KeyStore + +class MockServerHttpsTest extends Specification { + + RemoteMockServer remoteMockServer + + HttpMockServer httpMockServer + + @Shared + SSLContext sslContext = SSLContexts.custom() + .loadTrustMaterial(trustStore()) + .build() + + @Shared + CloseableHttpClient client = HttpClients.custom() + .setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) + .setSslcontext(sslContext) + .build() + + def setup() { + httpMockServer = new HttpMockServer(19000) + remoteMockServer = new RemoteMockServer('localhost', 19000) + } + + def cleanup() { + httpMockServer.stop() + } + + def 'should handle HTTPS server' () { + expect: + remoteMockServer.addMock(new AddMock( + name: 'testHttps', + path: 'testEndpoint', + port: 10443, + predicate: '''{req -> req.xml.name() == 'request'}''', + response: '''{req -> ""}''', + https: new Https( + keyPassword: 'changeit', + keystorePassword: 'changeit', + keystorePath: MockServerHttpsTest.classLoader.getResource('keystore.jks').path + ), + soap: false + )) + when: + HttpPost restPost = new HttpPost('https://localhost:10443/testEndpoint') + restPost.entity = new StringEntity('', ContentType.create("text/xml", "UTF-8")) + CloseableHttpResponse response = client.execute(restPost) + then: + GPathResult restPostResponse = Util.extractXmlResponse(response) + restPostResponse.name() == 'goodResponse-request' + and: + remoteMockServer.removeMock('testHttps')?.size() == 1 + } + + private KeyStore trustStore() { + KeyStore truststore = KeyStore.getInstance(KeyStore.defaultType) + truststore.load(new FileInputStream(MockServerHttpsTest.classLoader.getResource('truststore.jks').path), "changeit".toCharArray()); + return truststore + } +} diff --git a/mockserver-tests/src/test/resources/keystore.jks b/mockserver-tests/src/test/resources/keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..d5e35d1551194e1ced94cb1f249b8decfa66aa0a GIT binary patch literal 2257 zcmc(gX*kr29>?cD3o-U3O9qi*I>XG^mpFr%GWLCMY>mbesZJ;iMp5i8xBaZLn*Pa&yxKlP>n>7CAy@#LoIJ^s?%3^}d*dwO4oQeB zBfPWB*Iy4*Bs_h?*e=idB>zQCreCGnUlPX8$xQ=8%OP{(uVv?o#H$44Sv(e)lf2ey zJ7inI{d5(b!85)w@Xf<~no6QW%zpEjitAk{wiPm~xxhC+rJD3MWCot+AvmKrJ}281 zAqVL1uo~T$Z?{#iKIV(W#*B^_EkOB_?c+Iwl9@F1PDG2aYU%c9c;?!Al9?fmDLX{I zMK7%7E84GCp3>60C(aH;Kgihj+8b4@-{^n7;x}G~XZ(5`q^D|rEOR-qOsq)Xn%viv zpW@cUaG*znRE2i^IMDksFacqpYBG*wli#%LU;GPX%@G6r#wt_Z;2sQDY#4GS7M0=+ zW4a#PNId1SBi0rmkUQ1mRF-HkYh@2PVH92LR$H@GZ%pwX>CF~M19ntzjE8~p}hiV-36bTAvWFJGK zabJ|fa`$Kxi>5Z;xnByEur11gHb<{#;9w|nYIMnG`?reY9(sN37Sz%}`0Sj$wohr8 zmD<~wq&Ii?YUWxR2ww5<20laETO;ZEb+1JuZ+o^Hby0JTBXSitT9?uyS|5% zNwZ&%RcCWt?%Kqm){Yy9+KUkg_j-)S9g)?5~x+k-$){8E(`y*fmIJSO1@Jgt@3{qIE$a zNFtg7i9=JsfkhB700u)P?vELu`MG$IR6KQy9{}Jm5IPJ4NAqw&9HCsIU>gEP1TFNp zIVAeOh5aw%{t4s#34{NHL4IIpSqXlO4jO~i#pqyl(HJ)ge(c{?`#<*oaS{bk_%WD+ zzX72Df*>>n;003v0Mz`8;2^tLi*>>8+b+BztJ|@-wA>D@AWd4^a+P|Yf4Mg;RP1be zwDQhZCK1oHY@lT@r3zd$tVb?yF4juxE+eAo3WJ9q#-aRiUiFag@|IIVj2;+t)|Ojg z4q0V(z1jDVZ5Zo0QbiW|JnKT`w{HsV`hpQ(-$xIUkvXNGJEv~gMY^r(HcXP%In0ky z;PQ$qD=`jWOJts#`8+vzdz!7C^(>a}kU3XIJ>pj_a?cJ~Vk*)(2A`sQ`TJW?=MfPk zqb~e}w9=={;R|qg=Dvkd)O4?ADQ+J|yhb&E{8b81E+>v)Gw>Kw5=y3B^Hu-EX)AeA z7w@nj!YFkX3;{qu$x*Z%8gWn$gaGs~R7_&g>EX!ybX^M9DkH3O_Y*z3#qq}w588DQ zA^?)BRD*^D+nlq1xq3@g%?TI;PF3zTuW z?R}7i{cjHLxLkI;{mIPDYH8&5}-V}wK8;kd? z+pnx)Gm8X8Yf%j-0gJ4))7%S}Prx addMock(co) @@ -108,7 +109,7 @@ class HttpMockServer { throw new RuntimeException('mock already registered') } Mock mock = mockFromRequest(request) - HttpServerWrapper child = getOrCreateChildServer(mock.port) + HttpServerWrapper child = getOrCreateChildServer(mock.port, mock.https) child.addMock(mock) saveConfiguration(request) mockNames << name @@ -121,7 +122,7 @@ class HttpMockServer { throw new RuntimeException('mock already registered') } Mock mock = mockFromConfig(co) - HttpServerWrapper child = getOrCreateChildServer(mock.port) + HttpServerWrapper child = getOrCreateChildServer(mock.port, null) child.addMock(mock) configuration.put(name, co) mockNames << name @@ -156,6 +157,7 @@ class HttpMockServer { mock.responseHeaders = request.responseHeaders mock.schema = request.schema mock.preserveHistory = request.preserveHistory != false + mock.https = request.https return mock } @@ -173,10 +175,10 @@ class HttpMockServer { return mock } - private HttpServerWrapper getOrCreateChildServer(int mockPort) { + private HttpServerWrapper getOrCreateChildServer(int mockPort, Https https) { HttpServerWrapper child = childServers[mockPort] if (!child) { - child = new HttpServerWrapper(mockPort, executor) + child = new HttpServerWrapper(mockPort, executor, https) childServers.put(mockPort, child) } return child diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy index 6f8f9f4..9121c6b 100644 --- a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy +++ b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy @@ -2,9 +2,17 @@ package pl.touk.mockserver.server import com.sun.net.httpserver.HttpHandler import com.sun.net.httpserver.HttpServer +import com.sun.net.httpserver.HttpsConfigurator +import com.sun.net.httpserver.HttpsServer import groovy.transform.PackageScope import groovy.util.logging.Slf4j +import pl.touk.mockserver.api.common.Https +import javax.net.ssl.KeyManagerFactory +import javax.net.ssl.SSLContext +import javax.net.ssl.TrustManager +import java.security.KeyStore +import java.security.SecureRandom import java.util.concurrent.Executor @Slf4j @@ -15,16 +23,37 @@ class HttpServerWrapper { private List executors = [] - HttpServerWrapper(int port, Executor executor) { + HttpServerWrapper(int port, Executor executor, Https https) { this.port = port InetSocketAddress addr = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), port) - httpServer = HttpServer.create(addr, 0) + httpServer = buildServer(addr, https) httpServer.executor = executor log.info("Http server starting on port $port...") httpServer.start() log.info('Http server is started') } + private HttpServer buildServer(InetSocketAddress addr, Https https) { + if (https) { + HttpsServer httpsServer = HttpsServer.create(addr, 0) + httpsServer.httpsConfigurator = new HttpsConfigurator(buildSslContext(https)) + return httpsServer + } else { + return HttpServer.create(addr, 0) + } + } + + private SSLContext buildSslContext(Https https) { + KeyStore keyStore = KeyStore.getInstance(KeyStore.defaultType) + keyStore.load(new FileInputStream(https.keystorePath), https.keystorePassword.toCharArray()) + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.defaultAlgorithm) + kmf.init(keyStore, https.keyPassword.toCharArray()) + + SSLContext ssl = SSLContext.getInstance('TLSv1') + ssl.init(kmf.keyManagers, [] as TrustManager[], new SecureRandom()) + return ssl + } + void createContext(String context, HttpHandler handler) { httpServer.createContext(context, handler) } diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/Mock.groovy b/mockserver/src/main/groovy/pl/touk/mockserver/server/Mock.groovy index ed37f89..e132ac9 100644 --- a/mockserver/src/main/groovy/pl/touk/mockserver/server/Mock.groovy +++ b/mockserver/src/main/groovy/pl/touk/mockserver/server/Mock.groovy @@ -5,6 +5,7 @@ import groovy.transform.PackageScope import groovy.util.logging.Slf4j import org.codehaus.groovy.control.CompilerConfiguration import org.codehaus.groovy.control.customizers.ImportCustomizer +import pl.touk.mockserver.api.common.Https import pl.touk.mockserver.api.common.Method import javax.xml.XMLConstants @@ -35,6 +36,7 @@ class Mock implements Comparable { private Validator validator Map imports = [:] boolean preserveHistory = true + Https https Mock(String name, String path, int port) { if (!(name)) { From 79e75303908d2eb41518821be46d4ae65b914c3e Mon Sep 17 00:00:00 2001 From: Piotr Fus Date: Mon, 29 Jan 2018 21:14:11 +0100 Subject: [PATCH 3/7] Add https client authentication Change-Id: Ib04eaa8e534e2ac83fb4c11a169f110a5f3a580d --- .../xsd/pl/touk/mockserver/api/common.xsd | 3 + .../tests/MockServerHttpsTest.groovy | 99 ++++++++++++++++-- .../src/test/resources/trusted.jks | Bin 0 -> 2235 bytes .../src/test/resources/truststore.jks | Bin 0 -> 1880 bytes .../src/test/resources/untrusted.jks | Bin 0 -> 2255 bytes .../server/HttpServerWrapper.groovy | 29 ++++- .../touk/mockserver/server/HttpsConfig.groovy | 28 +++++ 7 files changed, 145 insertions(+), 14 deletions(-) create mode 100644 mockserver-tests/src/test/resources/trusted.jks create mode 100644 mockserver-tests/src/test/resources/truststore.jks create mode 100644 mockserver-tests/src/test/resources/untrusted.jks create mode 100644 mockserver/src/main/groovy/pl/touk/mockserver/server/HttpsConfig.groovy diff --git a/mockserver-api/src/main/xsd/pl/touk/mockserver/api/common.xsd b/mockserver-api/src/main/xsd/pl/touk/mockserver/api/common.xsd index 953b839..673be7d 100644 --- a/mockserver-api/src/main/xsd/pl/touk/mockserver/api/common.xsd +++ b/mockserver-api/src/main/xsd/pl/touk/mockserver/api/common.xsd @@ -24,6 +24,9 @@ + + + diff --git a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerHttpsTest.groovy b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerHttpsTest.groovy index 91b851a..0bef47f 100644 --- a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerHttpsTest.groovy +++ b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerHttpsTest.groovy @@ -16,8 +16,10 @@ import pl.touk.mockserver.client.Util import pl.touk.mockserver.server.HttpMockServer import spock.lang.Shared import spock.lang.Specification +import spock.lang.Unroll import javax.net.ssl.SSLContext +import javax.net.ssl.SSLHandshakeException import java.security.KeyStore class MockServerHttpsTest extends Specification { @@ -27,14 +29,20 @@ class MockServerHttpsTest extends Specification { HttpMockServer httpMockServer @Shared - SSLContext sslContext = SSLContexts.custom() + SSLContext noClientAuthSslContext = SSLContexts.custom() .loadTrustMaterial(trustStore()) .build() @Shared - CloseableHttpClient client = HttpClients.custom() - .setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) - .setSslcontext(sslContext) + SSLContext trustedCertificateSslContext = SSLContexts.custom() + .loadKeyMaterial(trustedCertificateKeystore(), 'changeit'.toCharArray()) + .loadTrustMaterial(trustStore()) + .build() + + @Shared + SSLContext untrustedCertificateSslContext = SSLContexts.custom() + .loadKeyMaterial(untrustedCertificateKeystore(), 'changeit'.toCharArray()) + .loadTrustMaterial(trustStore()) .build() def setup() { @@ -64,17 +72,90 @@ class MockServerHttpsTest extends Specification { when: HttpPost restPost = new HttpPost('https://localhost:10443/testEndpoint') restPost.entity = new StringEntity('', ContentType.create("text/xml", "UTF-8")) - CloseableHttpResponse response = client.execute(restPost) + CloseableHttpResponse response = client(noClientAuthSslContext).execute(restPost) then: GPathResult restPostResponse = Util.extractXmlResponse(response) restPostResponse.name() == 'goodResponse-request' - and: - remoteMockServer.removeMock('testHttps')?.size() == 1 + } + + def 'should handle HTTPS server with client auth' () { + expect: + remoteMockServer.addMock(new AddMock( + name: 'testHttps', + path: 'testEndpoint', + port: 10443, + predicate: '''{req -> req.xml.name() == 'request'}''', + response: '''{req -> ""}''', + https: new Https( + keyPassword: 'changeit', + keystorePassword: 'changeit', + keystorePath: MockServerHttpsTest.classLoader.getResource('keystore.jks').path, + truststorePath: MockServerHttpsTest.classLoader.getResource('truststore.jks').path, + truststorePassword: 'changeit', + requireClientAuth: true + ), + soap: false + )) + when: + HttpPost restPost = new HttpPost('https://localhost:10443/testEndpoint') + restPost.entity = new StringEntity('', ContentType.create("text/xml", "UTF-8")) + CloseableHttpResponse response = client(trustedCertificateSslContext).execute(restPost) + then: + GPathResult restPostResponse = Util.extractXmlResponse(response) + restPostResponse.name() == 'goodResponse-request' + } + + @Unroll + def 'should handle HTTPS server with wrong client auth' () { + expect: + remoteMockServer.addMock(new AddMock( + name: 'testHttps', + path: 'testEndpoint', + port: 10443, + predicate: '''{req -> req.xml.name() == 'request'}''', + response: '''{req -> ""}''', + https: new Https( + keyPassword: 'changeit', + keystorePassword: 'changeit', + keystorePath: MockServerHttpsTest.classLoader.getResource('keystore.jks').path, + truststorePath: MockServerHttpsTest.classLoader.getResource('truststore.jks').path, + truststorePassword: 'changeit', + requireClientAuth: true + ), + soap: false + )) + when: + HttpPost restPost = new HttpPost('https://localhost:10443/testEndpoint') + restPost.entity = new StringEntity('', ContentType.create("text/xml", "UTF-8")) + client(sslContext).execute(restPost) + then: + thrown(SSLHandshakeException) + where: + sslContext << [noClientAuthSslContext, untrustedCertificateSslContext] + } + + private CloseableHttpClient client(SSLContext sslContext) { + return HttpClients.custom() + .setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) + .setSslcontext(sslContext) + .build() + } + + private KeyStore trustedCertificateKeystore() { + return loadKeystore('trusted.jks') + } + + private KeyStore untrustedCertificateKeystore() { + return loadKeystore('untrusted.jks') } private KeyStore trustStore() { - KeyStore truststore = KeyStore.getInstance(KeyStore.defaultType) - truststore.load(new FileInputStream(MockServerHttpsTest.classLoader.getResource('truststore.jks').path), "changeit".toCharArray()); + return loadKeystore('truststore.jks') + } + + private KeyStore loadKeystore(String fileName) { + KeyStore truststore = KeyStore.getInstance(KeyStore.defaultType) + truststore.load(new FileInputStream(MockServerHttpsTest.classLoader.getResource(fileName).path), "changeit".toCharArray()); return truststore } } diff --git a/mockserver-tests/src/test/resources/trusted.jks b/mockserver-tests/src/test/resources/trusted.jks new file mode 100644 index 0000000000000000000000000000000000000000..e6fa704c24008010af9739e39947c464b0c58b26 GIT binary patch literal 2235 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 literal 0 HcmV?d00001 diff --git a/mockserver-tests/src/test/resources/untrusted.jks b/mockserver-tests/src/test/resources/untrusted.jks new file mode 100644 index 0000000000000000000000000000000000000000..ca94b454c5d413ac28dbcf921a5b678eb20896a5 GIT binary patch literal 2255 zcmc(gX*kr29>?cz#xNNBQWQd6hL{<<7(&QSS(6T8h-(?k8D$-eP-Mx3v9?)eLLn^} zB8@HTpzJ3p3{qySgX5g%-skqB_xHv3#rN~-_k5q<^Zo8E?=6EsAdUk8{|FWt9D$1r zkMO;GK)g)SSI&Y!oFF6t!bb8#d6c051VC^I0RSfmLV(OGZ&JRCH8R4@^J^DW8`_3_ z6rWi>v{S9NoI!bfsxbh%!D-lhck{xHgzm4F`ofr$A%t{j5 zdc0=sU3`1phd6T`3$b&HimkERIZfVAy3#GxALgM%6)?5r;%jNw6xJ8k_DcBBwPaVv zQ?7%r)s=P3^yE8?#e;h>1(c5Tq6j6XS=N%6rP})QE)# z--?D8&4V3+ZFN8_TdqDUz?0qv%rcRhb<^$r9vi z_dDnEWWa7GcA{ZDZ$wmm#uePdT#MUl-uhyzx)czlPJ@haBC=CxBH?`7{h`RYCUgn+ zB-Vck7mi6;PrMg1FUd9~{n@E`gAkPPCCY-wW7nK``3B~+OU)I7--4Lqzu&K%=KR5; zR@t5XF#LIZeEMr{i=S-}Z~xQ@TCL%Vs_G&2m->=XH%^FJL91(L4IVjU@v6SX!}e?yC76w~E|)0H8s3&r zNYd5JE8Z)}UY+jP;=QMc@!%X3c}I&WSw4l6a8&ndR4XA@{;O_5aM`rKH-BiPQeTH~ z!FQ``?DLusjeFY9rcGew`Ki@p94B_SxT^pgQaA*vx!LdK^vng3 ztP!#z=*|^jN`_|I?`rKEzR-jzxc+!xN%wY?D_@a|-si$;oGfb@|9nX~J&cNH8?%^u zu%q}lZYbW{$1iPvOv~vJW=TXH?bYMFc-BwtzKqwx9i%*cl$yM__~ClQsC+&h?>WpY zKGr?W(5Gpw)LsZ%JyVNKxr^}Sz8O^B-gibulwGu+%PNg{>w!l(LP}@iDXaUWxO}HQDl}GF6R&nF(y3aDf4CyM5d&e=A03)sBDree>QlQP1WwYlZvIXcB#!Y z`t9%dS*=@-MV#wnwL}WAP8|FxemO6clgw(iaP;3+`#<*oaS{QL`#G3{)c_L! zfrB{#csK|E0HR*a@y3nEJSY^;9)a2fPB+sEbrp5nzoX|`^Qa#+wXjtpyv zf6r6k6!Ttm6YLoFO1Gq8uw}>ogt1|4g)w%SNDf1~O?}owG1BG-e z0jO3(_A3VEiE%t1$btTQ*#h%Xo)qQcxe7Hl2B0CoB7G_bD*6W>pRVHaa|<$SnWWOG z8o636{^c&c&4?E}qt91=lrE)F`#-29AE$h+&zYrK^{GLkE8q$afqn0TPq?%=e9KQbX|(gsBXRbvB;F4Lp=DdgAf6b z8)ItfjFEyO5j*Q{CvzNwpy!Ez1wGPD`0i(3LSQmve z6h-!@kDm-vl7lk2ge$!w^X@)nRkv}=b@I5elZ>EM{tI1n%*@B9R#hEpZH`%CdmHN-9^-;H_Artw!Fcc;HOhxwt?)s||HqqyZYN(KMPJDq#mZzvJ! z6RK-A0_wMsUa3A0&FVv5G80c!ys=TW)_*95(`|27pf8$eNqO8vu5RAv`;yvJAqxuu WR1{UN&PgWK`+(=#3qkv-?7slilILmw literal 0 HcmV?d00001 diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy index 9121c6b..0ab3ca2 100644 --- a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy +++ b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy @@ -2,15 +2,16 @@ package pl.touk.mockserver.server import com.sun.net.httpserver.HttpHandler import com.sun.net.httpserver.HttpServer -import com.sun.net.httpserver.HttpsConfigurator import com.sun.net.httpserver.HttpsServer import groovy.transform.PackageScope import groovy.util.logging.Slf4j import pl.touk.mockserver.api.common.Https +import javax.net.ssl.KeyManager import javax.net.ssl.KeyManagerFactory import javax.net.ssl.SSLContext import javax.net.ssl.TrustManager +import javax.net.ssl.TrustManagerFactory import java.security.KeyStore import java.security.SecureRandom import java.util.concurrent.Executor @@ -36,7 +37,7 @@ class HttpServerWrapper { private HttpServer buildServer(InetSocketAddress addr, Https https) { if (https) { HttpsServer httpsServer = HttpsServer.create(addr, 0) - httpsServer.httpsConfigurator = new HttpsConfigurator(buildSslContext(https)) + httpsServer.httpsConfigurator = new HttpsConfig(buildSslContext(https), https) return httpsServer } else { return HttpServer.create(addr, 0) @@ -44,14 +45,32 @@ class HttpServerWrapper { } private SSLContext buildSslContext(Https https) { + KeyManager[] keyManagers = buildKeyManager(https) + TrustManager[] trustManagers = buildTrustManager(https) + + SSLContext ssl = SSLContext.getInstance('TLSv1') + ssl.init(keyManagers, trustManagers, new SecureRandom()) + return ssl + } + + private KeyManager[] buildKeyManager(Https https) { KeyStore keyStore = KeyStore.getInstance(KeyStore.defaultType) keyStore.load(new FileInputStream(https.keystorePath), https.keystorePassword.toCharArray()) KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.defaultAlgorithm) kmf.init(keyStore, https.keyPassword.toCharArray()) + return kmf.keyManagers + } - SSLContext ssl = SSLContext.getInstance('TLSv1') - ssl.init(kmf.keyManagers, [] as TrustManager[], new SecureRandom()) - return ssl + private TrustManager[] buildTrustManager(Https https) { + if (https.requireClientAuth) { + KeyStore trustStore = KeyStore.getInstance(KeyStore.defaultType) + trustStore.load(new FileInputStream(https.truststorePath), https.truststorePassword.toCharArray()) + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.defaultAlgorithm) + tmf.init(trustStore) + return tmf.trustManagers + } else { + return [] + } } void createContext(String context, HttpHandler handler) { diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpsConfig.groovy b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpsConfig.groovy new file mode 100644 index 0000000..68b5550 --- /dev/null +++ b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpsConfig.groovy @@ -0,0 +1,28 @@ +package pl.touk.mockserver.server + +import com.sun.net.httpserver.HttpsConfigurator +import com.sun.net.httpserver.HttpsParameters +import groovy.transform.CompileStatic +import pl.touk.mockserver.api.common.Https + +import javax.net.ssl.SSLContext +import javax.net.ssl.SSLParameters + +@CompileStatic +class HttpsConfig extends HttpsConfigurator { + private final Https https + + HttpsConfig(SSLContext sslContext, Https https) { + super(sslContext) + this.https = https + } + + @Override + void configure(HttpsParameters httpsParameters) { + SSLContext sslContext = getSSLContext() + SSLParameters sslParameters = sslContext.defaultSSLParameters + sslParameters.needClientAuth = https.requireClientAuth + httpsParameters.needClientAuth = https.requireClientAuth + httpsParameters.SSLParameters = sslParameters + } +} From 0b7d0b52bc09bfa694a9ea95d4b4ea49111aa80c Mon Sep 17 00:00:00 2001 From: Piotr Fus Date: Tue, 30 Jan 2018 10:58:18 +0100 Subject: [PATCH 4/7] Add spock-global-unroll Change-Id: I0642bdf0e269f60c46d0b9c19b3d29af5a8c786b --- mockserver-tests/pom.xml | 4 ++++ .../mockserver/tests/MockServerHttpsTest.groovy | 17 +++++------------ .../tests/MockServerIntegrationTest.groovy | 6 ------ .../mockserver/server/HttpMockServer.groovy | 2 +- .../mockserver/server/HttpServerWrapper.groovy | 2 +- pom.xml | 7 +++++++ 6 files changed, 18 insertions(+), 20 deletions(-) diff --git a/mockserver-tests/pom.xml b/mockserver-tests/pom.xml index f09c8cd..ffc9d6c 100644 --- a/mockserver-tests/pom.xml +++ b/mockserver-tests/pom.xml @@ -44,6 +44,10 @@ mockserver-client ${project.version} + + info.solidsoft.spock + spock-global-unroll + diff --git a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerHttpsTest.groovy b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerHttpsTest.groovy index 0bef47f..28a070a 100644 --- a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerHttpsTest.groovy +++ b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerHttpsTest.groovy @@ -16,7 +16,6 @@ import pl.touk.mockserver.client.Util import pl.touk.mockserver.server.HttpMockServer import spock.lang.Shared import spock.lang.Specification -import spock.lang.Unroll import javax.net.ssl.SSLContext import javax.net.ssl.SSLHandshakeException @@ -24,9 +23,9 @@ import java.security.KeyStore class MockServerHttpsTest extends Specification { - RemoteMockServer remoteMockServer + RemoteMockServer remoteMockServer = new RemoteMockServer('localhost', 19000) - HttpMockServer httpMockServer + HttpMockServer httpMockServer = new HttpMockServer(19000) @Shared SSLContext noClientAuthSslContext = SSLContexts.custom() @@ -45,17 +44,12 @@ class MockServerHttpsTest extends Specification { .loadTrustMaterial(trustStore()) .build() - def setup() { - httpMockServer = new HttpMockServer(19000) - remoteMockServer = new RemoteMockServer('localhost', 19000) - } - def cleanup() { httpMockServer.stop() } def 'should handle HTTPS server' () { - expect: + given: remoteMockServer.addMock(new AddMock( name: 'testHttps', path: 'testEndpoint', @@ -79,7 +73,7 @@ class MockServerHttpsTest extends Specification { } def 'should handle HTTPS server with client auth' () { - expect: + given: remoteMockServer.addMock(new AddMock( name: 'testHttps', path: 'testEndpoint', @@ -105,9 +99,8 @@ class MockServerHttpsTest extends Specification { restPostResponse.name() == 'goodResponse-request' } - @Unroll def 'should handle HTTPS server with wrong client auth' () { - expect: + given: remoteMockServer.addMock(new AddMock( name: 'testHttps', path: 'testEndpoint', diff --git a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy index ab16248..05cd546 100644 --- a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy +++ b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy @@ -29,7 +29,6 @@ import pl.touk.mockserver.client.Util import pl.touk.mockserver.server.HttpMockServer import spock.lang.Shared import spock.lang.Specification -import spock.lang.Unroll class MockServerIntegrationTest extends Specification { @@ -244,7 +243,6 @@ class MockServerIntegrationTest extends Specification { soapPostResponse.Body.'goodResponseSoap-request'.size() == 1 } - @Unroll def "should dispatch rest mocks when second on #name"() { given: remoteMockServer.addMock(new AddMock( @@ -283,7 +281,6 @@ class MockServerIntegrationTest extends Specification { 9998 | 'test2' | 'another port and path' } - @Unroll def "should dispatch rest mock with response code"() { given: remoteMockServer.addMock(new AddMock( @@ -857,7 +854,6 @@ class MockServerIntegrationTest extends Specification { mockEvents2[0].response.statusCode == 202 } - @Unroll def "should return mock report with #mockEvents events when deleting mock with flag skip mock = #skipReport"() { expect: remoteMockServer.addMock(new AddMock( @@ -885,7 +881,6 @@ class MockServerIntegrationTest extends Specification { true | 0 } - @Unroll def "should reject mock when it has System.exit in closure"() { when: remoteMockServer.addMock(new AddMock( @@ -1133,7 +1128,6 @@ class MockServerIntegrationTest extends Specification { remoteMockServer.removeMock('testRest')?.size() == 1 } - @Unroll def 'should handle leading slash'() { given: String name = "testRest-${UUID.randomUUID().toString()}" diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy index 859b712..58b7573 100644 --- a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy +++ b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy @@ -42,7 +42,7 @@ class HttpMockServer { HttpMockServer(int port = 9999, ConfigObject initialConfiguration = new ConfigObject(), int threads = 10) { executor = Executors.newFixedThreadPool(threads) - httpServerWrapper = new HttpServerWrapper(port, executor, null) + httpServerWrapper = new HttpServerWrapper(port, executor) initialConfiguration.values()?.each { ConfigObject co -> addMock(co) diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy index 0ab3ca2..a5e939c 100644 --- a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy +++ b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy @@ -24,7 +24,7 @@ class HttpServerWrapper { private List executors = [] - HttpServerWrapper(int port, Executor executor, Https https) { + HttpServerWrapper(int port, Executor executor, Https https = null) { this.port = port InetSocketAddress addr = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), port) httpServer = buildServer(addr, https) diff --git a/pom.xml b/pom.xml index 43ce7cd..c99b3f7 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,7 @@ 1.11.2 2.5.2 1.4 + 0.5.1 @@ -99,6 +100,12 @@ ${jmh.version} test + + info.solidsoft.spock + spock-global-unroll + ${spock-global-unroll.version} + test + From 4d4d303d58bf729df7e06d690b27c76e40d4f31e Mon Sep 17 00:00:00 2001 From: Piotr Fus Date: Tue, 30 Jan 2018 11:09:21 +0100 Subject: [PATCH 5/7] Change keystore type to JKS Change-Id: I8cdb7a22d9bec61906b6a9acd409079cd6dd2dcf --- .../touk/mockserver/tests/MockServerIntegrationTest.groovy | 6 ++---- .../pl/touk/mockserver/server/HttpServerWrapper.groovy | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy index 05cd546..e9f2d42 100644 --- a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy +++ b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy @@ -27,6 +27,7 @@ import pl.touk.mockserver.client.MockDoesNotExist import pl.touk.mockserver.client.RemoteMockServer import pl.touk.mockserver.client.Util import pl.touk.mockserver.server.HttpMockServer +import spock.lang.AutoCleanup import spock.lang.Shared import spock.lang.Specification @@ -34,6 +35,7 @@ class MockServerIntegrationTest extends Specification { RemoteMockServer remoteMockServer + @AutoCleanup('stop') HttpMockServer httpMockServer @Shared @@ -44,10 +46,6 @@ class MockServerIntegrationTest extends Specification { remoteMockServer = new RemoteMockServer('localhost', 9000) } - def cleanup() { - httpMockServer.stop() - } - def "should add working rest mock on endpoint"() { expect: remoteMockServer.addMock(new AddMock( diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy index a5e939c..c9e63e9 100644 --- a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy +++ b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWrapper.groovy @@ -54,7 +54,7 @@ class HttpServerWrapper { } private KeyManager[] buildKeyManager(Https https) { - KeyStore keyStore = KeyStore.getInstance(KeyStore.defaultType) + KeyStore keyStore = KeyStore.getInstance('jks') keyStore.load(new FileInputStream(https.keystorePath), https.keystorePassword.toCharArray()) KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.defaultAlgorithm) kmf.init(keyStore, https.keyPassword.toCharArray()) @@ -63,7 +63,7 @@ class HttpServerWrapper { private TrustManager[] buildTrustManager(Https https) { if (https.requireClientAuth) { - KeyStore trustStore = KeyStore.getInstance(KeyStore.defaultType) + KeyStore trustStore = KeyStore.getInstance('jks') trustStore.load(new FileInputStream(https.truststorePath), https.truststorePassword.toCharArray()) TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.defaultAlgorithm) tmf.init(trustStore) From 76a3ecacad5bc7341f58b649f485bf76f44af517 Mon Sep 17 00:00:00 2001 From: Piotr Fus Date: Tue, 30 Jan 2018 11:42:17 +0100 Subject: [PATCH 6/7] Add HTTPS docs Change-Id: Ic97f453fe71ea04f099abe92272eaaf3a41e10fe --- README.md | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c9ee033..2552e18 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,21 @@ testRest { path='testEndpoint' name='testRest' } +testHttps { + soap=false + port=10443 + path='testHttps' + name='testHttps' + method='GET' + https={ + keystorePath='/tmp/keystore.jks' + keystorePassword='keystorePass' + keyPassword='keyPass' + truststorePath='/tmp/truststore.jks' + truststorePassword='truststorePass' + requireClientAuth=true + } +} ``` ### Build with docker @@ -96,7 +111,15 @@ remoteMockServer.addMock(new AddMock( statusCode: ..., method: ..., responseHeaders: ..., - schema: ... + schema: ..., + https: new Https( + keystorePath: '/tmp/keystore.jks', + keystorePassword: 'keystorePass', + keyPassword: 'keyPass', + truststorePath: '/tmp/truststore.jks', + truststorePassword: 'truststorePass', + requireClientAuth: true + ) )) ``` @@ -117,6 +140,14 @@ Send POST request to localhost:/serverControl ... ... + + /tmp/keystore.jks + keystorePass + keyPass + /tmp/truststore.jks + truststorePass + true + ``` @@ -133,6 +164,18 @@ Send POST request to localhost:/serverControl - responseHeaders - groovyClosure as string which must evaluate to Map which will be added to response headers, default { _ -> \[:] } - schema - path to xsd schema file on mockserver classpath; default empty, so no vallidation of request is performed; if validation fails then response has got status 400 and response is raw message from validator - imports - list of imports for closures (each import is separate tag); `alias` is the name of `fullClassName` available in closure; `fullClassName` must be available on classpath of mock server +- https - HTTPS configuration + +#### HTTPS configuration + +- keystorePath - path to keystore in JKS format, keystore should contains only one privateKeyEntry +- keystorePassword - keystore password +- keyPassword - key password +- truststorePath - path to truststore in JKS format +- truststorePassword - truststore password +- requireClientAuth - whether client auth is required (two-way SSL) + +**HTTP** and **HTTPS** should be started on separated ports. ### Closures request properties From d070ea820bb615624a320b04941e500dbefbbfac Mon Sep 17 00:00:00 2001 From: Piotr Fus Date: Mon, 26 Feb 2018 10:39:34 +0100 Subject: [PATCH 7/7] Handle HTTPS from config Change-Id: I48ea5ea2e35e62906b7b5ec5c5e624d06192172b --- .../touk/mockserver/tests/MockServerHttpsTest.groovy | 6 ++---- .../pl/touk/mockserver/server/HttpMockServer.groovy | 12 +++++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerHttpsTest.groovy b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerHttpsTest.groovy index 28a070a..8d6eff8 100644 --- a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerHttpsTest.groovy +++ b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerHttpsTest.groovy @@ -14,6 +14,7 @@ import pl.touk.mockserver.api.request.AddMock import pl.touk.mockserver.client.RemoteMockServer import pl.touk.mockserver.client.Util import pl.touk.mockserver.server.HttpMockServer +import spock.lang.AutoCleanup import spock.lang.Shared import spock.lang.Specification @@ -25,6 +26,7 @@ class MockServerHttpsTest extends Specification { RemoteMockServer remoteMockServer = new RemoteMockServer('localhost', 19000) + @AutoCleanup('stop') HttpMockServer httpMockServer = new HttpMockServer(19000) @Shared @@ -44,10 +46,6 @@ class MockServerHttpsTest extends Specification { .loadTrustMaterial(trustStore()) .build() - def cleanup() { - httpMockServer.stop() - } - def 'should handle HTTPS server' () { given: remoteMockServer.addMock(new AddMock( diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy index 58b7573..6a57f12 100644 --- a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy +++ b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy @@ -122,7 +122,7 @@ class HttpMockServer { throw new RuntimeException('mock already registered') } Mock mock = mockFromConfig(co) - HttpServerWrapper child = getOrCreateChildServer(mock.port, null) + HttpServerWrapper child = getOrCreateChildServer(mock.port, mock.https) child.addMock(mock) configuration.put(name, co) mockNames << name @@ -172,6 +172,16 @@ class HttpMockServer { mock.responseHeaders = co.responseHeaders ?: null mock.schema = co.schema ?: null mock.preserveHistory = co.preserveHistory != false + if (co.https) { + mock.https = new Https( + keystorePath: co.https.keystorePath ?: null, + keystorePassword: co.https.keystorePassword, + keyPassword: co.https.keyPassword, + truststorePath: co.https.truststorePath, + truststorePassword: co.https.truststorePassword, + requireClientAuth: co.https?.requireClientAuth?.asBoolean() ?: false + ) + } return mock }