Refactort server and client
This commit is contained in:
parent
f72e510fea
commit
fda5b6ca5c
13 changed files with 188 additions and 96 deletions
|
@ -1,38 +1,29 @@
|
|||
package pl.touk.mockserver.server
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange
|
||||
import groovy.util.slurpersupport.GPathResult
|
||||
import groovy.transform.PackageScope
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
@PackageScope
|
||||
class ContextExecutor {
|
||||
private final HttpServerWraper httpServerWraper
|
||||
final String path
|
||||
private final List<Mock> mocks
|
||||
|
||||
ContextExecutor(HttpServerWraper httpServerWraper, String path, Mock initialMock) {
|
||||
ContextExecutor(HttpServerWraper httpServerWraper, Mock initialMock) {
|
||||
this.httpServerWraper = httpServerWraper
|
||||
this.path = path
|
||||
this.path = initialMock.path
|
||||
this.mocks = new CopyOnWriteArrayList<>([initialMock])
|
||||
httpServerWraper.createContext(path, {
|
||||
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"
|
||||
for (Mock mock : mocks) {
|
||||
try {
|
||||
if (ex.requestMethod == mock.method &&
|
||||
mock.predicate(request)) {
|
||||
println "Mock ${mock.name} invoked"
|
||||
++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()
|
||||
}
|
||||
if (mock.match(ex.requestMethod, request)) {
|
||||
MockResponse httpResponse = mock.apply(request)
|
||||
fillExchange(ex, httpResponse)
|
||||
return
|
||||
}
|
||||
} 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) {
|
||||
Mock mock = mocks.find { it.name == name }
|
||||
if (mock) {
|
||||
|
@ -57,11 +60,4 @@ class ContextExecutor {
|
|||
void addMock(Mock 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>"""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +1,34 @@
|
|||
package pl.touk.mockserver.server
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.util.slurpersupport.GPathResult
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import java.util.concurrent.CopyOnWriteArraySet
|
||||
|
||||
@PackageScope
|
||||
class HttpMockServer {
|
||||
|
||||
private final HttpServerWraper httpServerWraper
|
||||
private final List<HttpServerWraper> childServers = new CopyOnWriteArrayList<>()
|
||||
private final Set<String> mockNames = new CopyOnWriteArraySet<>()
|
||||
|
||||
HttpMockServer(int port = 9999){
|
||||
httpServerWraper= new HttpServerWraper(port)
|
||||
HttpMockServer(int port = 9999) {
|
||||
httpServerWraper = new HttpServerWraper(port)
|
||||
|
||||
httpServerWraper.createContext('/serverControl', {
|
||||
HttpExchange ex ->
|
||||
try{
|
||||
try {
|
||||
GPathResult request = new XmlSlurper().parse(ex.requestBody)
|
||||
if(ex.requestMethod== 'POST' && request.name() == 'addMock'){
|
||||
if (ex.requestMethod == 'POST' && request.name() == 'addMock') {
|
||||
addMock(request, ex)
|
||||
}else if(ex.requestMethod == 'POST' && request.name() == 'removeMock'){
|
||||
} else if (ex.requestMethod == 'POST' && request.name() == 'removeMock') {
|
||||
removeMock(request, ex)
|
||||
}
|
||||
//TODO add get mock report
|
||||
//TODO add list mock
|
||||
}catch(Exception e){
|
||||
} catch (Exception e) {
|
||||
createErrorResponse(ex, e)
|
||||
}
|
||||
})
|
||||
|
@ -37,53 +39,45 @@ class HttpMockServer {
|
|||
if (name in mockNames) {
|
||||
throw new RuntimeException('mock already registered')
|
||||
}
|
||||
println "Adding $name"
|
||||
String mockPath = request.path
|
||||
int mockPort = Integer.valueOf(request.port as String)
|
||||
Mock mock = new Mock(name, mockPath, mockPort)
|
||||
String predicate = request.predicate
|
||||
if(predicate){
|
||||
mock.predicate = Eval.me(predicate) as Closure
|
||||
}
|
||||
String okResponse = request.response
|
||||
if(okResponse){
|
||||
mock.responseOk = Eval.me(okResponse) as Closure
|
||||
}
|
||||
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
|
||||
}
|
||||
HttpServerWraper child = childServers.find { it.port == mockPort }
|
||||
if (!child) {
|
||||
child = new HttpServerWraper(mockPort)
|
||||
childServers << child
|
||||
}
|
||||
child.addMock(mockPath, mock)
|
||||
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
|
||||
int mockPort = Integer.valueOf(request.port as String)
|
||||
Mock mock = new Mock(name, mockPath, mockPort)
|
||||
mock.predicate = request.predicate
|
||||
mock.response = request.response
|
||||
mock.soap = request.soap
|
||||
mock.statusCode = request.statusCode
|
||||
mock.method = request.method
|
||||
mock.responseHeaders = request.responseHeaders
|
||||
return mock
|
||||
}
|
||||
|
||||
private HttpServerWraper getOrCreateChildServer(int mockPort) {
|
||||
HttpServerWraper child = childServers.find { it.port == mockPort }
|
||||
if (!child) {
|
||||
child = new HttpServerWraper(mockPort)
|
||||
childServers << child
|
||||
}
|
||||
return child
|
||||
}
|
||||
|
||||
private void removeMock(GPathResult request, HttpExchange ex) {
|
||||
String name = request.name
|
||||
if (! (name in mockNames)) {
|
||||
if (!(name in mockNames)) {
|
||||
throw new RuntimeException('mock not registered')
|
||||
}
|
||||
println "Removing $name"
|
||||
int used = childServers.inject(0) { int res, HttpServerWraper server-> server.removeMock(name) + res}
|
||||
int used = childServers.inject(0) { int res, HttpServerWraper server -> server.removeMock(name) + res }
|
||||
mockNames.remove(name)
|
||||
ex.sendResponseHeaders(200, 0)
|
||||
ex.responseBody << "<mockRemoved>$used</mockRemoved>"
|
||||
|
@ -96,8 +90,8 @@ class HttpMockServer {
|
|||
ex.responseBody.close()
|
||||
}
|
||||
|
||||
void stop(){
|
||||
childServers.each {it.stop()}
|
||||
void stop() {
|
||||
childServers.each { it.stop() }
|
||||
httpServerWraper.stop()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,14 @@ package pl.touk.mockserver.server
|
|||
|
||||
import com.sun.net.httpserver.HttpHandler
|
||||
import com.sun.net.httpserver.HttpServer
|
||||
import groovy.transform.PackageScope
|
||||
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
@PackageScope
|
||||
class HttpServerWraper {
|
||||
private final HttpServer httpServer
|
||||
private final int port
|
||||
final int port
|
||||
|
||||
private List<ContextExecutor> executors = []
|
||||
|
||||
|
@ -16,22 +18,23 @@ class HttpServerWraper {
|
|||
InetSocketAddress addr = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), port)
|
||||
httpServer = HttpServer.create(addr, 0)
|
||||
httpServer.executor = Executors.newCachedThreadPool()
|
||||
println "Http server statrting on port $port..."
|
||||
println("Http server statrting on port $port...")
|
||||
httpServer.start()
|
||||
println 'Http server is started'
|
||||
println('Http server is started')
|
||||
}
|
||||
|
||||
void createContext(String context, HttpHandler handler) {
|
||||
httpServer.createContext(context, handler)
|
||||
}
|
||||
|
||||
void addMock(String path, Mock mock) {
|
||||
ContextExecutor executor = executors.find { it.path == path }
|
||||
void addMock(Mock mock) {
|
||||
ContextExecutor executor = executors.find { it.path == mock.path }
|
||||
if (executor) {
|
||||
executor.addMock(mock)
|
||||
} else {
|
||||
executors << new ContextExecutor(this, path, mock)
|
||||
executors << new ContextExecutor(this, mock)
|
||||
}
|
||||
println "Added mock ${mock.name}"
|
||||
}
|
||||
|
||||
void stop() {
|
||||
|
@ -40,6 +43,6 @@ class HttpServerWraper {
|
|||
}
|
||||
|
||||
int removeMock(String name) {
|
||||
executors.inject(0) { int res, ContextExecutor e -> e.removeMock(name) + res}
|
||||
executors.inject(0) { int res, ContextExecutor e -> e.removeMock(name) + res }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package pl.touk.mockserver.server
|
||||
|
||||
class Main {
|
||||
static void main(String [] args) {
|
||||
static void main(String[] args) {
|
||||
HttpMockServer httpMockServer = new HttpMockServer()
|
||||
|
||||
Runtime.runtime.addShutdownHook(new Thread({
|
||||
|
@ -10,7 +10,7 @@ class Main {
|
|||
println 'Http server is stopped'
|
||||
} as Runnable))
|
||||
|
||||
while(true){
|
||||
while (true) {
|
||||
Thread.sleep(10000)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
package pl.touk.mockserver.server
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.PackageScope
|
||||
|
||||
@PackageScope
|
||||
@EqualsAndHashCode(excludes = ["counter"])
|
||||
class Mock {
|
||||
final String name
|
||||
final String path
|
||||
final int port
|
||||
Closure predicate = { _ -> true }
|
||||
Closure responseOk = { _ -> '' }
|
||||
Closure response = { _ -> '' }
|
||||
Closure responseHeaders = { _ -> [:] }
|
||||
boolean soap = false
|
||||
int statusCode = 200
|
||||
String method = 'POST'
|
||||
Closure responseHeaders = {_ -> [:]}
|
||||
int counter = 0
|
||||
|
||||
Mock(String name, String path, int port) {
|
||||
|
@ -20,4 +22,60 @@ class Mock {
|
|||
this.path = path
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,41 +2,43 @@ package pl.touk.mockserver.server
|
|||
|
||||
import com.sun.net.httpserver.Headers
|
||||
import groovy.json.JsonSlurper
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.util.slurpersupport.GPathResult
|
||||
|
||||
class Request {
|
||||
@PackageScope
|
||||
class MockRequest {
|
||||
final String text
|
||||
final Map<String,String> headers
|
||||
final Map<String,String> query
|
||||
final Map<String, String> headers
|
||||
final Map<String, String> query
|
||||
final GPathResult xml
|
||||
final GPathResult soap
|
||||
final Object json
|
||||
|
||||
Request(String text, Headers headers, String query) {
|
||||
MockRequest(String text, Headers headers, String query) {
|
||||
this.text = text
|
||||
this.headers = headersToMap(headers)
|
||||
this.query = queryParamsToMap(query)
|
||||
this.xml = inputToXml(text)
|
||||
this.soap = inputToSoap(xml)
|
||||
this.json= inputToJson(text)
|
||||
this.json = inputToJson(text)
|
||||
}
|
||||
|
||||
private static GPathResult inputToXml(String text) {
|
||||
try{
|
||||
try {
|
||||
return new XmlSlurper().parseText(text)
|
||||
}catch (Exception _){
|
||||
} catch (Exception _) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private static GPathResult inputToSoap(GPathResult xml) {
|
||||
try{
|
||||
try {
|
||||
if (xml.name() == 'Envelope' && xml.Body.size() > 0) {
|
||||
return getSoapBodyContent(xml)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}catch (Exception _){
|
||||
} catch (Exception _) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
@ -46,9 +48,9 @@ class Request {
|
|||
}
|
||||
|
||||
private static Object inputToJson(String text) {
|
||||
try{
|
||||
try {
|
||||
return new JsonSlurper().parseText(text)
|
||||
}catch (Exception _){
|
||||
} catch (Exception _) {
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
package pl.touk.mockserver.client
|
||||
|
||||
class InvalidMockDefinitionException extends RuntimeException{
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.TypeChecked
|
||||
|
||||
@CompileStatic
|
||||
@TypeChecked
|
||||
class InvalidMockDefinitionException extends RuntimeException {
|
||||
InvalidMockDefinitionException(String s) {
|
||||
super(s)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
package pl.touk.mockserver.client
|
||||
|
||||
class MockAlreadyExists extends RuntimeException{}
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.TypeChecked
|
||||
|
||||
@CompileStatic
|
||||
@TypeChecked
|
||||
class MockAlreadyExists extends RuntimeException {}
|
|
@ -1,4 +1,9 @@
|
|||
package pl.touk.mockserver.client
|
||||
|
||||
class MockDoesNotExist extends RuntimeException{
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.TypeChecked
|
||||
|
||||
@CompileStatic
|
||||
@TypeChecked
|
||||
class MockDoesNotExist extends RuntimeException {
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
package pl.touk.mockserver.client
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.TypeChecked
|
||||
|
||||
@CompileStatic
|
||||
@TypeChecked
|
||||
class RemoveMockRequestData {
|
||||
String name
|
||||
}
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
package pl.touk.mockserver.client
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.TypeChecked
|
||||
import groovy.util.slurpersupport.GPathResult
|
||||
import org.apache.http.HttpEntity
|
||||
import org.apache.http.client.methods.CloseableHttpResponse
|
||||
import org.apache.http.util.EntityUtils
|
||||
|
||||
@CompileStatic
|
||||
@TypeChecked
|
||||
class Util {
|
||||
static GPathResult extractXmlResponse(CloseableHttpResponse response){
|
||||
static GPathResult extractXmlResponse(CloseableHttpResponse response) {
|
||||
HttpEntity entity = response.entity
|
||||
GPathResult xml = new XmlSlurper().parseText(EntityUtils.toString(entity))
|
||||
EntityUtils.consumeQuietly(entity)
|
||||
|
|
|
@ -334,8 +334,8 @@ class MockServerIntegrationTest extends Specification {
|
|||
when:
|
||||
CloseableHttpResponse response = client.execute(restHead)
|
||||
then:
|
||||
response.statusLine.statusCode == 200
|
||||
EntityUtils.consumeQuietly(response.entity)
|
||||
response.statusLine.statusCode == 200
|
||||
}
|
||||
|
||||
def "should dispatch rest mock with options method"() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue