Add https publishing
Change-Id: I7611b7379c0f42d02342adc1eec56c0d79caa432
This commit is contained in:
parent
96a294828b
commit
0323749ff4
7 changed files with 129 additions and 7 deletions
|
@ -18,5 +18,13 @@
|
||||||
<xs:attribute name="alias" type="xs:string"/>
|
<xs:attribute name="alias" type="xs:string"/>
|
||||||
<xs:attribute name="fullClassName" type="xs:string"/>
|
<xs:attribute name="fullClassName" type="xs:string"/>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="https">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="keystorePath" type="xs:string" />
|
||||||
|
<xs:element name="keystorePassword" type="xs:string" />
|
||||||
|
<xs:element name="keyPassword" type="xs:string" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
</xs:schema>
|
</xs:schema>
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
<xs:element name="soap" type="xs:boolean" minOccurs="0"/>
|
<xs:element name="soap" type="xs:boolean" minOccurs="0"/>
|
||||||
<xs:element name="statusCode" type="xs:int" minOccurs="0"/>
|
<xs:element name="statusCode" type="xs:int" minOccurs="0"/>
|
||||||
<xs:element name="method" type="common:method" minOccurs="0"/>
|
<xs:element name="method" type="common:method" minOccurs="0"/>
|
||||||
|
<xs:element name="https" type="common:https" minOccurs="0" />
|
||||||
<xs:element name="responseHeaders" type="xs:string" minOccurs="0"/>
|
<xs:element name="responseHeaders" type="xs:string" minOccurs="0"/>
|
||||||
<xs:element name="schema" type="xs:string" minOccurs="0"/>
|
<xs:element name="schema" type="xs:string" minOccurs="0"/>
|
||||||
<xs:element name="imports" type="common:importAlias" minOccurs="0" maxOccurs="unbounded"/>
|
<xs:element name="imports" type="common:importAlias" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
|
|
@ -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 -> "<goodResponse-${req.xml.name()}/>"}''',
|
||||||
|
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('<request/>', 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
|
||||||
|
}
|
||||||
|
}
|
BIN
mockserver-tests/src/test/resources/keystore.jks
Normal file
BIN
mockserver-tests/src/test/resources/keystore.jks
Normal file
Binary file not shown.
|
@ -2,6 +2,7 @@ package pl.touk.mockserver.server
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange
|
import com.sun.net.httpserver.HttpExchange
|
||||||
import groovy.util.logging.Slf4j
|
import groovy.util.logging.Slf4j
|
||||||
|
import pl.touk.mockserver.api.common.Https
|
||||||
import pl.touk.mockserver.api.common.ImportAlias
|
import pl.touk.mockserver.api.common.ImportAlias
|
||||||
import pl.touk.mockserver.api.common.Method
|
import pl.touk.mockserver.api.common.Method
|
||||||
import pl.touk.mockserver.api.request.AddMock
|
import pl.touk.mockserver.api.request.AddMock
|
||||||
|
@ -41,7 +42,7 @@ class HttpMockServer {
|
||||||
|
|
||||||
HttpMockServer(int port = 9999, ConfigObject initialConfiguration = new ConfigObject(), int threads = 10) {
|
HttpMockServer(int port = 9999, ConfigObject initialConfiguration = new ConfigObject(), int threads = 10) {
|
||||||
executor = Executors.newFixedThreadPool(threads)
|
executor = Executors.newFixedThreadPool(threads)
|
||||||
httpServerWrapper = new HttpServerWrapper(port, executor)
|
httpServerWrapper = new HttpServerWrapper(port, executor, null)
|
||||||
|
|
||||||
initialConfiguration.values()?.each { ConfigObject co ->
|
initialConfiguration.values()?.each { ConfigObject co ->
|
||||||
addMock(co)
|
addMock(co)
|
||||||
|
@ -108,7 +109,7 @@ class HttpMockServer {
|
||||||
throw new RuntimeException('mock already registered')
|
throw new RuntimeException('mock already registered')
|
||||||
}
|
}
|
||||||
Mock mock = mockFromRequest(request)
|
Mock mock = mockFromRequest(request)
|
||||||
HttpServerWrapper child = getOrCreateChildServer(mock.port)
|
HttpServerWrapper child = getOrCreateChildServer(mock.port, mock.https)
|
||||||
child.addMock(mock)
|
child.addMock(mock)
|
||||||
saveConfiguration(request)
|
saveConfiguration(request)
|
||||||
mockNames << name
|
mockNames << name
|
||||||
|
@ -121,7 +122,7 @@ class HttpMockServer {
|
||||||
throw new RuntimeException('mock already registered')
|
throw new RuntimeException('mock already registered')
|
||||||
}
|
}
|
||||||
Mock mock = mockFromConfig(co)
|
Mock mock = mockFromConfig(co)
|
||||||
HttpServerWrapper child = getOrCreateChildServer(mock.port)
|
HttpServerWrapper child = getOrCreateChildServer(mock.port, null)
|
||||||
child.addMock(mock)
|
child.addMock(mock)
|
||||||
configuration.put(name, co)
|
configuration.put(name, co)
|
||||||
mockNames << name
|
mockNames << name
|
||||||
|
@ -156,6 +157,7 @@ class HttpMockServer {
|
||||||
mock.responseHeaders = request.responseHeaders
|
mock.responseHeaders = request.responseHeaders
|
||||||
mock.schema = request.schema
|
mock.schema = request.schema
|
||||||
mock.preserveHistory = request.preserveHistory != false
|
mock.preserveHistory = request.preserveHistory != false
|
||||||
|
mock.https = request.https
|
||||||
return mock
|
return mock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,10 +175,10 @@ class HttpMockServer {
|
||||||
return mock
|
return mock
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpServerWrapper getOrCreateChildServer(int mockPort) {
|
private HttpServerWrapper getOrCreateChildServer(int mockPort, Https https) {
|
||||||
HttpServerWrapper child = childServers[mockPort]
|
HttpServerWrapper child = childServers[mockPort]
|
||||||
if (!child) {
|
if (!child) {
|
||||||
child = new HttpServerWrapper(mockPort, executor)
|
child = new HttpServerWrapper(mockPort, executor, https)
|
||||||
childServers.put(mockPort, child)
|
childServers.put(mockPort, child)
|
||||||
}
|
}
|
||||||
return child
|
return child
|
||||||
|
|
|
@ -2,9 +2,17 @@ package pl.touk.mockserver.server
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpHandler
|
import com.sun.net.httpserver.HttpHandler
|
||||||
import com.sun.net.httpserver.HttpServer
|
import com.sun.net.httpserver.HttpServer
|
||||||
|
import com.sun.net.httpserver.HttpsConfigurator
|
||||||
|
import com.sun.net.httpserver.HttpsServer
|
||||||
import groovy.transform.PackageScope
|
import groovy.transform.PackageScope
|
||||||
import groovy.util.logging.Slf4j
|
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
|
import java.util.concurrent.Executor
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ -15,16 +23,37 @@ class HttpServerWrapper {
|
||||||
|
|
||||||
private List<ContextExecutor> executors = []
|
private List<ContextExecutor> executors = []
|
||||||
|
|
||||||
HttpServerWrapper(int port, Executor executor) {
|
HttpServerWrapper(int port, Executor executor, Https https) {
|
||||||
this.port = port
|
this.port = port
|
||||||
InetSocketAddress addr = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), port)
|
InetSocketAddress addr = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), port)
|
||||||
httpServer = HttpServer.create(addr, 0)
|
httpServer = buildServer(addr, https)
|
||||||
httpServer.executor = executor
|
httpServer.executor = executor
|
||||||
log.info("Http server starting on port $port...")
|
log.info("Http server starting on port $port...")
|
||||||
httpServer.start()
|
httpServer.start()
|
||||||
log.info('Http server is started')
|
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) {
|
void createContext(String context, HttpHandler handler) {
|
||||||
httpServer.createContext(context, handler)
|
httpServer.createContext(context, handler)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import groovy.transform.PackageScope
|
||||||
import groovy.util.logging.Slf4j
|
import groovy.util.logging.Slf4j
|
||||||
import org.codehaus.groovy.control.CompilerConfiguration
|
import org.codehaus.groovy.control.CompilerConfiguration
|
||||||
import org.codehaus.groovy.control.customizers.ImportCustomizer
|
import org.codehaus.groovy.control.customizers.ImportCustomizer
|
||||||
|
import pl.touk.mockserver.api.common.Https
|
||||||
import pl.touk.mockserver.api.common.Method
|
import pl.touk.mockserver.api.common.Method
|
||||||
|
|
||||||
import javax.xml.XMLConstants
|
import javax.xml.XMLConstants
|
||||||
|
@ -35,6 +36,7 @@ class Mock implements Comparable<Mock> {
|
||||||
private Validator validator
|
private Validator validator
|
||||||
Map<String, String> imports = [:]
|
Map<String, String> imports = [:]
|
||||||
boolean preserveHistory = true
|
boolean preserveHistory = true
|
||||||
|
Https https
|
||||||
|
|
||||||
Mock(String name, String path, int port) {
|
Mock(String name, String path, int port) {
|
||||||
if (!(name)) {
|
if (!(name)) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue