Add https support

This commit is contained in:
Piotr Fus 2018-02-26 17:48:11 +01:00 committed by Dominik Przybysz
parent f8e0cc44f9
commit 0727ced422
17 changed files with 383 additions and 83 deletions

View file

@ -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<Mock> 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)

View file

@ -2,6 +2,7 @@ package pl.touk.mockserver.server
import com.sun.net.httpserver.HttpExchange
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.Method
import pl.touk.mockserver.api.request.AddMock
@ -30,8 +31,8 @@ import static pl.touk.mockserver.server.Util.createResponse
@Slf4j
class HttpMockServer {
private final HttpServerWraper httpServerWraper
private final Map<Integer, HttpServerWraper> childServers = new ConcurrentHashMap<>()
private final HttpServerWrapper httpServerWrapper
private final Map<Integer, HttpServerWrapper> childServers = new ConcurrentHashMap<>()
private final Set<String> mockNames = new CopyOnWriteArraySet<>()
private final ConfigObject configuration = new ConfigObject()
private final Executor executor
@ -41,13 +42,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 +109,7 @@ class HttpMockServer {
throw new RuntimeException('mock already registered')
}
Mock mock = mockFromRequest(request)
HttpServerWraper 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)
HttpServerWraper child = getOrCreateChildServer(mock.port)
HttpServerWrapper child = getOrCreateChildServer(mock.port, mock.https)
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
}
@ -170,13 +172,23 @@ 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
}
private HttpServerWraper getOrCreateChildServer(int mockPort) {
HttpServerWraper child = childServers[mockPort]
private HttpServerWrapper getOrCreateChildServer(int mockPort, Https https) {
HttpServerWrapper child = childServers[mockPort]
if (!child) {
child = new HttpServerWraper(mockPort, executor)
child = new HttpServerWrapper(mockPort, executor, https)
childServers.put(mockPort, child)
}
return child
@ -244,6 +256,6 @@ class HttpMockServer {
void stop() {
childServers.values().each { it.stop() }
httpServerWraper.stop()
httpServerWrapper.stop()
}
}

View file

@ -1,58 +0,0 @@
package pl.touk.mockserver.server
import com.sun.net.httpserver.HttpHandler
import com.sun.net.httpserver.HttpServer
import groovy.transform.PackageScope
import groovy.util.logging.Slf4j
import java.util.concurrent.Executor
@Slf4j
@PackageScope
class HttpServerWraper {
private final HttpServer httpServer
final int port
private List<ContextExecutor> executors = []
HttpServerWraper(int port, Executor executor) {
this.port = port
InetSocketAddress addr = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), port)
httpServer = HttpServer.create(addr, 0)
httpServer.executor = executor
log.info("Http server starting on port $port...")
httpServer.start()
log.info('Http server is started')
}
void createContext(String context, HttpHandler handler) {
httpServer.createContext(context, handler)
}
void addMock(Mock mock) {
ContextExecutor executor = executors.find { it.path == mock.path }
if (executor) {
executor.addMock(mock)
} else {
executors << new ContextExecutor(this, mock)
}
log.info("Added mock ${mock.name}")
}
void stop() {
executors.each { httpServer.removeContext(it.contextPath) }
httpServer.stop(0)
}
List<MockEvent> removeMock(String name) {
return executors.collect { it.removeMock(name) }.flatten() as List<MockEvent>
}
List<MockEvent> peekMock(String name) {
return executors.collect { it.peekMock(name) }.flatten() as List<MockEvent>
}
List<Mock> getMocks() {
return executors.collect { it.mocks }.flatten() as List<Mock>
}
}

View file

@ -0,0 +1,106 @@
package pl.touk.mockserver.server
import com.sun.net.httpserver.HttpHandler
import com.sun.net.httpserver.HttpServer
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
@Slf4j
@PackageScope
class HttpServerWrapper {
private final HttpServer httpServer
final int port
private List<ContextExecutor> executors = []
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)
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 HttpsConfig(buildSslContext(https), https)
return httpsServer
} else {
return HttpServer.create(addr, 0)
}
}
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('jks')
keyStore.load(new FileInputStream(https.keystorePath), https.keystorePassword.toCharArray())
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.defaultAlgorithm)
kmf.init(keyStore, https.keyPassword.toCharArray())
return kmf.keyManagers
}
private TrustManager[] buildTrustManager(Https https) {
if (https.requireClientAuth) {
KeyStore trustStore = KeyStore.getInstance('jks')
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) {
httpServer.createContext(context, handler)
}
void addMock(Mock mock) {
ContextExecutor executor = executors.find { it.path == mock.path }
if (executor) {
executor.addMock(mock)
} else {
executors << new ContextExecutor(this, mock)
}
log.info("Added mock ${mock.name}")
}
void stop() {
executors.each { httpServer.removeContext(it.contextPath) }
httpServer.stop(0)
}
List<MockEvent> removeMock(String name) {
return executors.collect { it.removeMock(name) }.flatten() as List<MockEvent>
}
List<MockEvent> peekMock(String name) {
return executors.collect { it.peekMock(name) }.flatten() as List<MockEvent>
}
List<Mock> getMocks() {
return executors.collect { it.mocks }.flatten() as List<Mock>
}
}

View file

@ -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
}
}

View file

@ -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<Mock> {
private Validator validator
Map<String, String> imports = [:]
boolean preserveHistory = true
Https https
Mock(String name, String path, int port) {
if (!(name)) {