Refactort server and client

This commit is contained in:
Dominik Adam Przybysz 2014-12-13 18:56:07 +01:00
parent f72e510fea
commit fda5b6ca5c
13 changed files with 188 additions and 96 deletions

View file

@ -1,38 +1,29 @@
package pl.touk.mockserver.server package pl.touk.mockserver.server
import com.sun.net.httpserver.HttpExchange import com.sun.net.httpserver.HttpExchange
import groovy.util.slurpersupport.GPathResult import groovy.transform.PackageScope
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
@PackageScope
class ContextExecutor { class ContextExecutor {
private final HttpServerWraper httpServerWraper private final HttpServerWraper httpServerWraper
final String path final String path
private final List<Mock> mocks private final List<Mock> mocks
ContextExecutor(HttpServerWraper httpServerWraper, String path, Mock initialMock) { ContextExecutor(HttpServerWraper httpServerWraper, Mock initialMock) {
this.httpServerWraper = httpServerWraper this.httpServerWraper = httpServerWraper
this.path = path this.path = initialMock.path
this.mocks = new CopyOnWriteArrayList<>([initialMock]) this.mocks = new CopyOnWriteArrayList<>([initialMock])
httpServerWraper.createContext(path, { httpServerWraper.createContext(path, {
HttpExchange ex -> HttpExchange ex ->
Request request = new Request(ex.requestBody.text, ex.requestHeaders, ex.requestURI.query) MockRequest request = new MockRequest(ex.requestBody.text, ex.requestHeaders, ex.requestURI.query)
println "Mock received input" println "Mock received input"
for (Mock mock : mocks) { for (Mock mock : mocks) {
try { try {
if (ex.requestMethod == mock.method && if (mock.match(ex.requestMethod, request)) {
mock.predicate(request)) { MockResponse httpResponse = mock.apply(request)
println "Mock ${mock.name} invoked" fillExchange(ex, httpResponse)
++mock.counter
String response = mock.responseOk(request)
mock.responseHeaders(request).each {
ex.responseHeaders.add(it.key as String, it.value as String)
}
ex.sendResponseHeaders(mock.statusCode, response ? 0 : -1)
if (response) {
ex.responseBody << (mock.soap ? wrapSoap(response) : response)
ex.responseBody.close()
}
return return
} }
} catch (Exception e) { } catch (Exception e) {
@ -45,6 +36,18 @@ class ContextExecutor {
}) })
} }
private static void fillExchange(HttpExchange httpExchange, MockResponse response) {
response.headers.each {
httpExchange.responseHeaders.add(it.key, it.value)
}
String responseText = response.response
httpExchange.sendResponseHeaders(response.statusCode, responseText ? responseText.length() : -1)
if (responseText) {
httpExchange.responseBody << responseText
httpExchange.responseBody.close()
}
}
int removeMock(String name) { int removeMock(String name) {
Mock mock = mocks.find { it.name == name } Mock mock = mocks.find { it.name == name }
if (mock) { if (mock) {
@ -57,11 +60,4 @@ class ContextExecutor {
void addMock(Mock mock) { void addMock(Mock mock) {
mocks << mock mocks << mock
} }
private static String wrapSoap(String request) {
"""<?xml version='1.0' encoding='UTF-8'?>
<soap-env:Envelope xmlns:soap-env='http://schemas.xmlsoap.org/soap/envelope/' xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
<soap-env:Body>${request}</soap-env:Body>
</soap-env:Envelope>"""
}
} }

View file

@ -1,11 +1,13 @@
package pl.touk.mockserver.server package pl.touk.mockserver.server
import com.sun.net.httpserver.HttpExchange import com.sun.net.httpserver.HttpExchange
import groovy.transform.PackageScope
import groovy.util.slurpersupport.GPathResult import groovy.util.slurpersupport.GPathResult
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.CopyOnWriteArraySet import java.util.concurrent.CopyOnWriteArraySet
@PackageScope
class HttpMockServer { class HttpMockServer {
private final HttpServerWraper httpServerWraper private final HttpServerWraper httpServerWraper
@ -37,44 +39,36 @@ class HttpMockServer {
if (name in mockNames) { if (name in mockNames) {
throw new RuntimeException('mock already registered') throw new RuntimeException('mock already registered')
} }
println "Adding $name" Mock mock = mockFromRequest(request)
HttpServerWraper child = getOrCreateChildServer(mock.port)
child.addMock(mock)
mockNames << name
ex.sendResponseHeaders(200, 0)
ex.responseBody << '<mockAdded/>'
ex.responseBody.close()
}
private static Mock mockFromRequest(GPathResult request) {
String name = request.name
String mockPath = request.path String mockPath = request.path
int mockPort = Integer.valueOf(request.port as String) int mockPort = Integer.valueOf(request.port as String)
Mock mock = new Mock(name, mockPath, mockPort) Mock mock = new Mock(name, mockPath, mockPort)
String predicate = request.predicate mock.predicate = request.predicate
if(predicate){ mock.response = request.response
mock.predicate = Eval.me(predicate) as Closure mock.soap = request.soap
} mock.statusCode = request.statusCode
String okResponse = request.response mock.method = request.method
if(okResponse){ mock.responseHeaders = request.responseHeaders
mock.responseOk = Eval.me(okResponse) as Closure return mock
}
String soap = request.soap
if(soap){
mock.soap = Boolean.valueOf(soap)
}
String statusCode = request.statusCode
if(statusCode){
mock.statusCode = Integer.valueOf(statusCode)
}
String method = request.method
if(method){
mock.method = method
}
String responseHeaders = request.responseHeaders
if(responseHeaders){
mock.responseHeaders = Eval.me(responseHeaders) as Closure
} }
private HttpServerWraper getOrCreateChildServer(int mockPort) {
HttpServerWraper child = childServers.find { it.port == mockPort } HttpServerWraper child = childServers.find { it.port == mockPort }
if (!child) { if (!child) {
child = new HttpServerWraper(mockPort) child = new HttpServerWraper(mockPort)
childServers << child childServers << child
} }
child.addMock(mockPath, mock) return child
mockNames << name
ex.sendResponseHeaders(200, 0)
ex.responseBody << '<mockAdded/>'
ex.responseBody.close()
} }
private void removeMock(GPathResult request, HttpExchange ex) { private void removeMock(GPathResult request, HttpExchange ex) {

View file

@ -2,12 +2,14 @@ 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 groovy.transform.PackageScope
import java.util.concurrent.Executors import java.util.concurrent.Executors
@PackageScope
class HttpServerWraper { class HttpServerWraper {
private final HttpServer httpServer private final HttpServer httpServer
private final int port final int port
private List<ContextExecutor> executors = [] private List<ContextExecutor> executors = []
@ -16,22 +18,23 @@ class HttpServerWraper {
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 = HttpServer.create(addr, 0)
httpServer.executor = Executors.newCachedThreadPool() httpServer.executor = Executors.newCachedThreadPool()
println "Http server statrting on port $port..." println("Http server statrting on port $port...")
httpServer.start() httpServer.start()
println 'Http server is started' println('Http server is started')
} }
void createContext(String context, HttpHandler handler) { void createContext(String context, HttpHandler handler) {
httpServer.createContext(context, handler) httpServer.createContext(context, handler)
} }
void addMock(String path, Mock mock) { void addMock(Mock mock) {
ContextExecutor executor = executors.find { it.path == path } ContextExecutor executor = executors.find { it.path == mock.path }
if (executor) { if (executor) {
executor.addMock(mock) executor.addMock(mock)
} else { } else {
executors << new ContextExecutor(this, path, mock) executors << new ContextExecutor(this, mock)
} }
println "Added mock ${mock.name}"
} }
void stop() { void stop() {

View file

@ -1,18 +1,20 @@
package pl.touk.mockserver.server package pl.touk.mockserver.server
import groovy.transform.EqualsAndHashCode import groovy.transform.EqualsAndHashCode
import groovy.transform.PackageScope
@PackageScope
@EqualsAndHashCode(excludes = ["counter"]) @EqualsAndHashCode(excludes = ["counter"])
class Mock { class Mock {
final String name final String name
final String path final String path
final int port final int port
Closure predicate = { _ -> true } Closure predicate = { _ -> true }
Closure responseOk = { _ -> '' } Closure response = { _ -> '' }
Closure responseHeaders = { _ -> [:] }
boolean soap = false boolean soap = false
int statusCode = 200 int statusCode = 200
String method = 'POST' String method = 'POST'
Closure responseHeaders = {_ -> [:]}
int counter = 0 int counter = 0
Mock(String name, String path, int port) { Mock(String name, String path, int port) {
@ -20,4 +22,60 @@ class Mock {
this.path = path this.path = path
this.port = port this.port = port
} }
boolean match(String method, MockRequest request) {
return this.method == method && predicate(request)
}
MockResponse apply(MockRequest request) {
println "Mock $name invoked"
++counter
String responseText = response(request)
String response = soap ? wrapSoap(responseText) : responseText
Map<String, String> headers = responseHeaders(request)
return new MockResponse(statusCode, response, headers)
}
private static String wrapSoap(String request) {
"""<?xml version='1.0' encoding='UTF-8'?>
<soap-env:Envelope xmlns:soap-env='http://schemas.xmlsoap.org/soap/envelope/' xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
<soap-env:Body>${request}</soap-env:Body>
</soap-env:Envelope>"""
}
void setPredicate(String predicate){
if (predicate) {
this.predicate = Eval.me(predicate) as Closure
}
}
void setResponse(String response){
if (response) {
this.response = Eval.me(response) as Closure
}
}
void setSoap(String soap){
if (soap) {
this.soap = Boolean.valueOf(soap)
}
}
void setStatusCode(String statusCode){
if (statusCode) {
this.statusCode = Integer.valueOf(statusCode)
}
}
void setMethod(String method){
if(method){
this.method = method
}
}
void setResponseHeaders(String responseHeaders){
if (responseHeaders) {
this.responseHeaders = Eval.me(responseHeaders) as Closure
}
}
} }

View file

@ -2,9 +2,11 @@ package pl.touk.mockserver.server
import com.sun.net.httpserver.Headers import com.sun.net.httpserver.Headers
import groovy.json.JsonSlurper import groovy.json.JsonSlurper
import groovy.transform.PackageScope
import groovy.util.slurpersupport.GPathResult import groovy.util.slurpersupport.GPathResult
class Request { @PackageScope
class MockRequest {
final String text final String text
final Map<String, String> headers final Map<String, String> headers
final Map<String, String> query final Map<String, String> query
@ -12,7 +14,7 @@ class Request {
final GPathResult soap final GPathResult soap
final Object json final Object json
Request(String text, Headers headers, String query) { MockRequest(String text, Headers headers, String query) {
this.text = text this.text = text
this.headers = headersToMap(headers) this.headers = headersToMap(headers)
this.query = queryParamsToMap(query) this.query = queryParamsToMap(query)

View file

@ -0,0 +1,16 @@
package pl.touk.mockserver.server
import groovy.transform.PackageScope
@PackageScope
class MockResponse {
final int statusCode
final String response
final Map<String, String> headers
MockResponse(int statusCode, String response, Map<String, String> headers) {
this.statusCode = statusCode
this.response = response
this.headers = headers
}
}

View file

@ -1,5 +1,10 @@
package pl.touk.mockserver.client package pl.touk.mockserver.client
import groovy.transform.CompileStatic
import groovy.transform.TypeChecked
@CompileStatic
@TypeChecked
class InvalidMockDefinitionException extends RuntimeException { class InvalidMockDefinitionException extends RuntimeException {
InvalidMockDefinitionException(String s) { InvalidMockDefinitionException(String s) {
super(s) super(s)

View file

@ -1,3 +1,8 @@
package pl.touk.mockserver.client package pl.touk.mockserver.client
import groovy.transform.CompileStatic
import groovy.transform.TypeChecked
@CompileStatic
@TypeChecked
class MockAlreadyExists extends RuntimeException {} class MockAlreadyExists extends RuntimeException {}

View file

@ -1,4 +1,9 @@
package pl.touk.mockserver.client package pl.touk.mockserver.client
import groovy.transform.CompileStatic
import groovy.transform.TypeChecked
@CompileStatic
@TypeChecked
class MockDoesNotExist extends RuntimeException { class MockDoesNotExist extends RuntimeException {
} }

View file

@ -1,5 +1,10 @@
package pl.touk.mockserver.client package pl.touk.mockserver.client
import groovy.transform.CompileStatic
import groovy.transform.TypeChecked
@CompileStatic
@TypeChecked
class RemoveMockRequestData { class RemoveMockRequestData {
String name String name
} }

View file

@ -1,12 +1,15 @@
package pl.touk.mockserver.client package pl.touk.mockserver.client
import groovy.json.JsonSlurper import groovy.json.JsonSlurper
import groovy.transform.PackageScope import groovy.transform.CompileStatic
import groovy.transform.TypeChecked
import groovy.util.slurpersupport.GPathResult import groovy.util.slurpersupport.GPathResult
import org.apache.http.HttpEntity import org.apache.http.HttpEntity
import org.apache.http.client.methods.CloseableHttpResponse import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.util.EntityUtils import org.apache.http.util.EntityUtils
@CompileStatic
@TypeChecked
class Util { class Util {
static GPathResult extractXmlResponse(CloseableHttpResponse response) { static GPathResult extractXmlResponse(CloseableHttpResponse response) {
HttpEntity entity = response.entity HttpEntity entity = response.entity

View file

@ -334,8 +334,8 @@ class MockServerIntegrationTest extends Specification {
when: when:
CloseableHttpResponse response = client.execute(restHead) CloseableHttpResponse response = client.execute(restHead)
then: then:
response.statusLine.statusCode == 200
EntityUtils.consumeQuietly(response.entity) EntityUtils.consumeQuietly(response.entity)
response.statusLine.statusCode == 200
} }
def "should dispatch rest mock with options method"() { def "should dispatch rest mock with options method"() {