From 0323749ff429d80bd364648c829cc10a8b175294 Mon Sep 17 00:00:00 2001 From: Piotr Fus Date: Mon, 29 Jan 2018 20:26:55 +0100 Subject: [PATCH] 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)) {