diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8b71f67
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+*.iml
+target/
+.idea
+
+.mvn/wrapper/maven-wrapper.jar
\ No newline at end of file
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000..f3283b0
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index bfa78aa..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-language: groovy
-
-jdk:
- - oraclejdk8
-
diff --git a/.woodpecker.yaml b/.woodpecker.yaml
new file mode 100644
index 0000000..5921e36
--- /dev/null
+++ b/.woodpecker.yaml
@@ -0,0 +1,59 @@
+variables:
+ &maven_image maven:3.9.6-eclipse-temurin-11-alpine
+
+when:
+ evaluate: 'not (CI_COMMIT_MESSAGE contains "Release")'
+
+steps:
+ - name: build
+ image: *maven_image
+ commands:
+ - mvn -B clean install -DskipTests -Dmaven.test.skip
+ - name: test
+ image: *maven_image
+ commands:
+ - mvn -B -pl :mockserver-tests verify
+ - name: deploy to public
+ image: *maven_image
+ commands:
+ - mvn -B jar:jar deploy:deploy
+ secrets: [reposilite_user, reposilite_token]
+ when:
+ branch: [dev, master]
+ - name: deploy to releases
+ image: woodpeckerci/plugin-gitea-release
+ settings:
+ base-url: https://git.ztsh.eu
+ files:
+ - "mockserver-client/target/mockserver-client*.jar"
+ - "mockserver/target/mockserver-full.jar"
+ api_key:
+ from_secret: git_pat
+ when:
+ - event: tag
+ - name: tag docker image
+ image: woodpeckerci/plugin-docker-buildx
+ settings:
+ platforms: linux/amd64,linux/arm64/v8,linux/ppc64le,linux/s390x
+ repo: ztsheu/http-mock-server
+ registry: docker.io
+ tags: ${CI_COMMIT_TAG}
+ username: ztsheu
+ password:
+ from_secret: docker_pat
+ when:
+ - event: tag
+ - name: build docker image
+ image: woodpeckerci/plugin-docker-buildx
+ settings:
+ platforms: linux/amd64,linux/arm64/v8,linux/ppc64le,linux/s390x
+ repo: ztsheu/http-mock-server
+ registry: docker.io
+ tags: latest
+ username: ztsheu
+ password:
+ from_secret: docker_pat
+ when:
+ - event: tag
+ - event: push
+ branch: dev
diff --git a/Dockerfile b/Dockerfile
index fc44fd1..3250eed 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM java:8
+FROM eclipse-temurin:11.0.22_7-jre-jammy
ADD mockserver/target/mockserver-full.jar /mockserver.jar
@@ -8,4 +8,4 @@ RUN mkdir /externalSchema
VOLUME /externalSchema
-CMD java -cp /mockserver.jar:/externalSchema -jar /mockserver.jar
+CMD java -cp /mockserver.jar:/externalSchema eu.ztsh.mockserver.server.Main
diff --git a/README.md b/README.md
index a06f405..cb67c96 100644
--- a/README.md
+++ b/README.md
@@ -1,25 +1,89 @@
[](https://travis-ci.org/TouK/http-mock-server)
-# HTTP MOCK SERVER
+HTTP MOCK SERVER
+================
-## Create server jar
+Http Mock Server allows to mock HTTP request using groovy closures.
+
+Create server jar
+-----------------
```
cd mockserver
mvn clean package assembly:single
```
-## Start server
+Start server
+------------
### Native start
```
-java -jar mockserver.jar [PORT]
+java -jar mockserver-full.jar [PORT] [CONFIGURATION_FILE]
```
Default port is 9999.
-### Start with docker
+If configuration file is passed then port must be defined.
+
+Configuration file is groovy configuration script e.g. :
+
+```groovy
+testRest2 {
+ port=9998
+ response='{ req -> \'\' }'
+ responseHeaders='{ _ -> [a: "b"] }'
+ path='testEndpoint'
+ predicate='{ req -> req.xml.name() == \'request1\'}'
+ name='testRest2'
+}
+testRest4 {
+ soap=true
+ port=9999
+ path='testEndpoint'
+ name='testRest4'
+ method='PUT'
+ statusCode=204
+}
+testRest3 {
+ port=9999
+ path='testEndpoint2'
+ name='testRest3'
+}
+testRest6 {
+ port=9999
+ path='testEndpoint2'
+ name='testRest6'
+ maxUses=1
+ cyclic=true
+}
+testRest {
+ imports {
+ aaa='bbb'
+ ccc='bla'
+ }
+ port=10001
+ path='testEndpoint'
+ name='testRest'
+}
+testHttps {
+ soap=false
+ port=10443
+ path='testHttps'
+ name='testHttps'
+ method='GET'
+ https={
+ keystorePath='/tmp/keystore.jks'
+ keystorePassword='keystorePass'
+ keyPassword='keyPass'
+ truststorePath='/tmp/truststore.jks'
+ truststorePassword='truststorePass'
+ requireClientAuth=true
+ }
+}
+```
+
+### Build with docker
Docker and docker-compose is needed.
@@ -28,7 +92,12 @@ Docker and docker-compose is needed.
docker-compose up -d
```
-## Create mock on server
+### Docker repoository
+
+Currently unavailable
+
+Create mock on server
+---------------------
### Via client
@@ -44,16 +113,26 @@ remoteMockServer.addMock(new AddMock(
statusCode: ...,
method: ...,
responseHeaders: ...,
- schema: ...
+ schema: ...,
+ maxUses: ...,
+ cyclic: ...,
+ https: new Https(
+ keystorePath: '/tmp/keystore.jks',
+ keystorePassword: 'keystorePass',
+ keyPassword: 'keyPass',
+ truststorePath: '/tmp/truststore.jks',
+ truststorePassword: 'truststorePass',
+ requireClientAuth: true
+ )
))
```
+
### Via HTTP
Send POST request to localhost:/serverControl
-
```xml
-
+
...
...
...
@@ -64,47 +143,75 @@ Send POST request to localhost:/serverControl
...
...
...
+
+ ...
+ ...
+
+ /tmp/keystore.jks
+ keystorePass
+ keyPass
+ /tmp/truststore.jks
+ truststorePass
+ true
+
```
### Parameters
-* name - name of mock, must be unique
-* path - path on which mock should be created
-* port - inteer, port on which mock should be created, cannot be the same as mock server port
-* predicate - groovy closure as string which must evaluate to true, when request object will be given to satisfy mock, optional, default {_ -> true}
-* response - groovy closure as string which must evaluate to string which will be response of mock when predicate is satisfied, optional, default { _ -> '' }
-* soap - true or false, is request and response should be wrapped in soap Envelope and Body elements, default false
-* statusCode - integer, status code of response when predicate is satisfied, default 200
-* method - POST|PUT|DELETE|GET|TRACE|OPTION|HEAD, expected http method of request, default POST
-* responseHeaders - groovyClosure as string which must evaluate to Map which will be added to response headers, default { _ -> [:] }
-* schema - path to xsd schema file on mockserver classpath; default empty, so no vallidation of request is performed; if validation fails then response has got status 400 and response is raw message from validator
+- name - name of mock, must be unique
+- path - path on which mock should be created
+- port - inteer, port on which mock should be created, cannot be the same as mock server port
+- predicate - groovy closure as string which must evaluate to true, when request object will be given to satisfy mock, optional, default {_ -> true}
+- response - groovy closure as string which must evaluate to string which will be response of mock when predicate is satisfied, optional, default { _ -> '' }
+- soap - true or false, is request and response should be wrapped in soap Envelope and Body elements, default false
+- statusCode - integer, status code of response when predicate is satisfied, default 200
+- method - POST|PUT|DELETE|GET|TRACE|OPTION|HEAD|ANY_METHOD, expected http method of request, default `POST`, `ANY_METHOD` matches all HTTP methods
+- responseHeaders - groovyClosure as string which must evaluate to Map which will be added to response headers, default { _ -> \[:] }
+- schema - path to xsd schema file on mockserver classpath; default empty, so no vallidation of request is performed; if validation fails then response has got status 400 and response is raw message from validator
+- imports - list of imports for closures (each import is separate tag); `alias` is the name of `fullClassName` available in closure; `fullClassName` must be available on classpath of mock server
+- https - HTTPS configuration
+- maxUses - limit uses of mock to the specific number, after that mock is marked as ignored (any negative number means unlimited - default, cannot set value to 0), after this number of invocation mock history is still available, but mock does not apply to any request
+- cyclic - should mock be added after `maxUses` uses at the end of the mock list (by default false)
+
+#### HTTPS configuration
+
+- keystorePath - path to keystore in JKS format, keystore should contains only one privateKeyEntry
+- keystorePassword - keystore password
+- keyPassword - key password
+- truststorePath - path to truststore in JKS format
+- truststorePassword - truststore password
+- requireClientAuth - whether client auth is required (two-way SSL)
+
+**HTTP** and **HTTPS** should be started on separated ports.
### Closures request properties
In closures input parameter (called req) contains properties:
-* text - request body as java.util.String
-* headers - java.util.Map with request headers
-* query - java.util.Map with query parameters
-* xml - groovy.util.slurpersupport.GPathResult created from request body (if request body is valid xml)
-* soap - groovy.util.slurpersupport.GPathResult created from request body without Envelope and Body elements (if request body is valid soap xml)
-* json - java.lang.Object created from request body (if request body is valid json)
-* path - java.util.List with not empty parts of request path
+- text - request body as java.util.String
+- headers - java.util.Map with request headers
+- query - java.util.Map with query parameters
+- xml - groovy.util.slurpersupport.GPathResult created from request body (if request body is valid xml)
+- soap - groovy.util.slurpersupport.GPathResult created from request body without Envelope and Body elements (if request body is valid soap xml)
+- json - java.lang.Object created from request body (if request body is valid json)
+- path - java.util.List with not empty parts of request path
Response if success:
```xml
-
+
```
Response with error message if failure:
```xml
-...
+...
```
-## Peek mock
+Peek mock
+---------
+
Mock could be peeked to get get report of its invocations.
### Via client
@@ -114,10 +221,11 @@ List mockEvents = remoteMockServer.peekMock('...')
```
### Via HTTP
+
Send POST request to localhost:/serverControl
```xml
-
+
...
```
@@ -125,7 +233,7 @@ Send POST request to localhost:/serverControl
Response if success:
```xml
-
+
...
@@ -157,10 +265,11 @@ Response if success:
Response with error message if failure:
```xml
-...
+...
```
-## Remove mock
+Remove mock
+-----------
When mock was used it could be unregistered by name. It also optionally returns report of mock invocations if second parameter is true.
@@ -169,11 +278,13 @@ When mock was used it could be unregistered by name. It also optionally returns
```java
List mockEvents = remoteMockServer.removeMock('...', ...)
```
+
### Via HTTP
+
Send POST request to localhost:/serverControl
```xml
-
+
...
...
@@ -182,7 +293,7 @@ Send POST request to localhost:/serverControl
Response if success (and skipReport not given or equal false):
```xml
-
+
...
@@ -214,16 +325,17 @@ Response if success (and skipReport not given or equal false):
If skipReport is set to true then response will be:
```xml
-
+
```
Response with error message if failure:
```xml
-...
+...
```
-## List mocks definitions
+List mocks definitions
+----------------------
### Via client
@@ -249,14 +361,71 @@ Response:
...
...
...
+
...
```
-## Remote repository
+Get mocks configuration
+-----------------------
-Mockserver is available at `philanthropist.touk.pl`.
+### Via client
+
+```java
+ConfigObject mocks = remoteMockServer.getConfiguration()
+```
+
+### Via HTTP
+
+Send GET request to localhost:/serverControl/configuration
+
+Response:
+
+```groovy
+testRest2 {
+ port=9998
+ response='{ req -> \'\' }'
+ responseHeaders='{ _ -> [a: "b"] }'
+ path='testEndpoint'
+ predicate='{ req -> req.xml.name() == \'request1\'}'
+ name='testRest2'
+}
+testRest4 {
+ soap=true
+ port=9999
+ path='testEndpoint'
+ name='testRest4'
+ method='PUT'
+ statusCode=204
+}
+testRest3 {
+ port=9999
+ path='testEndpoint2'
+ name='testRest3'
+}
+testRest6 {
+ port=9999
+ path='testEndpoint2'
+ name='testRest6'
+}
+testRest {
+ imports {
+ aaa='bbb'
+ ccc='bla'
+ }
+ port=10001
+ path='testEndpoint'
+ name='testRest'
+}
+```
+
+This response could be saved to file and passed as it is during mock server creation.
+
+Remote repository
+-----------------
+
+Mockserver is available at `philanthropist.ztsh.eu`.
Just add repository to maven pom:
@@ -267,10 +436,16 @@ Just add repository to maven pom:
...
touk
- https://philanthropist.touk.pl/nexus/content/repositories/releases
+ https://philanthropist.ztsh.eu/nexus/content/repositories/releases
...
...
```
+
+FAQ
+---
+
+Q: *Can I have two mocks returning responses interchangeably for the same request?*
+A: Yes, you can. Just set two mocks with `maxUses: 1` and `cyclic: true`.
diff --git a/mockserver-api/pom.xml b/mockserver-api/pom.xml
index 06cf7e7..de5029a 100644
--- a/mockserver-api/pom.xml
+++ b/mockserver-api/pom.xml
@@ -1,15 +1,21 @@
-
-
- http-mock-server
- pl.touk.mockserver
- 2.2.0
-
+
4.0.0
+
+ eu.ztsh.mockserver
+ http-mock-server
+ 3.0.0-SNAPSHOT
+
+
mockserver-api
+
+ jakarta.xml.bind
+ jakarta.xml.bind-api
+
org.projectlombok
lombok
@@ -18,19 +24,9 @@
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.3
-
- 1.8
- 1.8
-
-
org.codehaus.mojo
jaxb2-maven-plugin
- 2.2
xjc
diff --git a/mockserver-api/src/main/xjb/binding.xjb b/mockserver-api/src/main/xjb/binding.xjb
index a735d24..fdf2585 100644
--- a/mockserver-api/src/main/xjb/binding.xjb
+++ b/mockserver-api/src/main/xjb/binding.xjb
@@ -1,10 +1,9 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
+
diff --git a/mockserver-api/src/main/xsd/pl/touk/mockserver/api/common.xsd b/mockserver-api/src/main/xsd/eu/ztsh/mockserver/api/common.xsd
similarity index 55%
rename from mockserver-api/src/main/xsd/pl/touk/mockserver/api/common.xsd
rename to mockserver-api/src/main/xsd/eu/ztsh/mockserver/api/common.xsd
index ac21246..6dac2a9 100644
--- a/mockserver-api/src/main/xsd/pl/touk/mockserver/api/common.xsd
+++ b/mockserver-api/src/main/xsd/eu/ztsh/mockserver/api/common.xsd
@@ -1,5 +1,5 @@
-
+
@@ -11,6 +11,7 @@
+
@@ -18,5 +19,16 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mockserver-api/src/main/xsd/pl/touk/mockserver/api/request.xsd b/mockserver-api/src/main/xsd/eu/ztsh/mockserver/api/request.xsd
similarity index 77%
rename from mockserver-api/src/main/xsd/pl/touk/mockserver/api/request.xsd
rename to mockserver-api/src/main/xsd/eu/ztsh/mockserver/api/request.xsd
index 809cb44..891aebf 100644
--- a/mockserver-api/src/main/xsd/pl/touk/mockserver/api/request.xsd
+++ b/mockserver-api/src/main/xsd/eu/ztsh/mockserver/api/request.xsd
@@ -1,7 +1,7 @@
-
+
-
+
@@ -20,9 +20,13 @@
+
+
+
+
diff --git a/mockserver-api/src/main/xsd/pl/touk/mockserver/api/response.xsd b/mockserver-api/src/main/xsd/eu/ztsh/mockserver/api/response.xsd
similarity index 93%
rename from mockserver-api/src/main/xsd/pl/touk/mockserver/api/response.xsd
rename to mockserver-api/src/main/xsd/eu/ztsh/mockserver/api/response.xsd
index 152a293..66d4566 100644
--- a/mockserver-api/src/main/xsd/pl/touk/mockserver/api/response.xsd
+++ b/mockserver-api/src/main/xsd/eu/ztsh/mockserver/api/response.xsd
@@ -1,7 +1,7 @@
-
+
-
+
@@ -110,6 +110,7 @@
+
diff --git a/mockserver-client/pom.xml b/mockserver-client/pom.xml
index 997ed8a..cb7acfb 100644
--- a/mockserver-client/pom.xml
+++ b/mockserver-client/pom.xml
@@ -1,22 +1,44 @@
-
-
- http-mock-server
- pl.touk.mockserver
- 2.2.0
-
+
4.0.0
+
+ eu.ztsh.mockserver
+ http-mock-server
+ 3.0.0-SNAPSHOT
+
+
mockserver-client
-
- clean install
-
- org.codehaus.groovy
- groovy-all
+ eu.ztsh.mockserver
+ mockserver-api
+
+
+ org.apache.groovy
+ groovy
+
+
+ org.apache.groovy
+ groovy-json
+
+
+ org.apache.groovy
+ groovy-xml
+
+
+
+ org.glassfish.jaxb
+ jaxb-core
+
+
+ org.glassfish.jaxb
+ jaxb-runtime
+
+
org.apache.httpcomponents
httpclient
@@ -25,9 +47,15 @@
org.apache.commons
commons-lang3
-
- pl.touk.mockserver
- mockserver-api
-
+
+
+
+
+ org.codehaus.gmavenplus
+ gmavenplus-plugin
+
+
+
+
diff --git a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/InvalidMockDefinition.groovy b/mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/InvalidMockDefinition.groovy
similarity index 86%
rename from mockserver-client/src/main/groovy/pl/touk/mockserver/client/InvalidMockDefinition.groovy
rename to mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/InvalidMockDefinition.groovy
index f95b584..a39ef77 100644
--- a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/InvalidMockDefinition.groovy
+++ b/mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/InvalidMockDefinition.groovy
@@ -1,4 +1,4 @@
-package pl.touk.mockserver.client
+package eu.ztsh.mockserver.client
import groovy.transform.CompileStatic
import groovy.transform.TypeChecked
diff --git a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/InvalidMockRequestSchema.groovy b/mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/InvalidMockRequestSchema.groovy
similarity index 82%
rename from mockserver-client/src/main/groovy/pl/touk/mockserver/client/InvalidMockRequestSchema.groovy
rename to mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/InvalidMockRequestSchema.groovy
index ff3d401..b1de96f 100644
--- a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/InvalidMockRequestSchema.groovy
+++ b/mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/InvalidMockRequestSchema.groovy
@@ -1,4 +1,4 @@
-package pl.touk.mockserver.client
+package eu.ztsh.mockserver.client
import groovy.transform.CompileStatic
import groovy.transform.TypeChecked
diff --git a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/MockAlreadyExists.groovy b/mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/MockAlreadyExists.groovy
similarity index 82%
rename from mockserver-client/src/main/groovy/pl/touk/mockserver/client/MockAlreadyExists.groovy
rename to mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/MockAlreadyExists.groovy
index f0c00a9..40d5dce 100644
--- a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/MockAlreadyExists.groovy
+++ b/mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/MockAlreadyExists.groovy
@@ -1,4 +1,4 @@
-package pl.touk.mockserver.client
+package eu.ztsh.mockserver.client
import groovy.transform.CompileStatic
import groovy.transform.TypeChecked
diff --git a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/MockDoesNotExist.groovy b/mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/MockDoesNotExist.groovy
similarity index 82%
rename from mockserver-client/src/main/groovy/pl/touk/mockserver/client/MockDoesNotExist.groovy
rename to mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/MockDoesNotExist.groovy
index 02d0bee..fe93dc0 100644
--- a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/MockDoesNotExist.groovy
+++ b/mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/MockDoesNotExist.groovy
@@ -1,4 +1,4 @@
-package pl.touk.mockserver.client
+package eu.ztsh.mockserver.client
import groovy.transform.CompileStatic
import groovy.transform.TypeChecked
diff --git a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/RemoteMockServer.groovy b/mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/RemoteMockServer.groovy
similarity index 77%
rename from mockserver-client/src/main/groovy/pl/touk/mockserver/client/RemoteMockServer.groovy
rename to mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/RemoteMockServer.groovy
index ffbc9a0..f594614 100644
--- a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/RemoteMockServer.groovy
+++ b/mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/RemoteMockServer.groovy
@@ -1,4 +1,4 @@
-package pl.touk.mockserver.client
+package eu.ztsh.mockserver.client
import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.client.methods.HttpGet
@@ -7,13 +7,17 @@ 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.request.AddMock
-import pl.touk.mockserver.api.request.MockServerRequest
-import pl.touk.mockserver.api.request.PeekMock
-import pl.touk.mockserver.api.request.RemoveMock
-import pl.touk.mockserver.api.response.*
+import eu.ztsh.mockserver.api.request.AddMock
+import eu.ztsh.mockserver.api.request.MockServerRequest
+import eu.ztsh.mockserver.api.request.PeekMock
+import eu.ztsh.mockserver.api.request.RemoveMock
+import eu.ztsh.mockserver.api.response.MockEventReport
+import eu.ztsh.mockserver.api.response.MockPeeked
+import eu.ztsh.mockserver.api.response.MockRemoved
+import eu.ztsh.mockserver.api.response.MockReport
+import eu.ztsh.mockserver.api.response.Mocks
-import javax.xml.bind.JAXBContext
+import jakarta.xml.bind.JAXBContext
class RemoteMockServer {
private final String address
@@ -47,6 +51,13 @@ class RemoteMockServer {
return mockPeeked.mockEvents ?: []
}
+ ConfigObject getConfiguration() {
+ HttpGet get = new HttpGet(address + '/configuration')
+ CloseableHttpResponse response = client.execute(get)
+ String configuration = Util.extractStringResponse(response)
+ return new ConfigSlurper().parse(configuration)
+ }
+
private static StringEntity buildRemoveMockRequest(RemoveMock data) {
return new StringEntity(marshallRequest(data), ContentType.create("text/xml", "UTF-8"))
}
diff --git a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/Util.groovy b/mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/Util.groovy
similarity index 88%
rename from mockserver-client/src/main/groovy/pl/touk/mockserver/client/Util.groovy
rename to mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/Util.groovy
index 3a427d6..e79e0c3 100644
--- a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/Util.groovy
+++ b/mockserver-client/src/main/groovy/eu/ztsh/mockserver/client/Util.groovy
@@ -1,17 +1,18 @@
-package pl.touk.mockserver.client
+package eu.ztsh.mockserver.client
import groovy.json.JsonSlurper
import groovy.transform.CompileStatic
import groovy.transform.TypeChecked
-import groovy.util.slurpersupport.GPathResult
+import groovy.xml.XmlSlurper
+import groovy.xml.slurpersupport.GPathResult
import org.apache.http.HttpEntity
import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.util.EntityUtils
-import pl.touk.mockserver.api.response.ExceptionOccured
-import pl.touk.mockserver.api.response.MockAdded
-import pl.touk.mockserver.api.response.MockServerResponse
+import eu.ztsh.mockserver.api.response.ExceptionOccured
+import eu.ztsh.mockserver.api.response.MockAdded
+import eu.ztsh.mockserver.api.response.MockServerResponse
-import javax.xml.bind.JAXBContext
+import jakarta.xml.bind.JAXBContext
@CompileStatic
@TypeChecked
diff --git a/mockserver-tests/pom.xml b/mockserver-tests/pom.xml
index 43ad941..2506a7a 100644
--- a/mockserver-tests/pom.xml
+++ b/mockserver-tests/pom.xml
@@ -1,49 +1,68 @@
-
+
+ 4.0.0
+
http-mock-server
- pl.touk.mockserver
- 2.2.0
+ eu.ztsh.mockserver
+ 3.0.0-SNAPSHOT
- 4.0.0
mockserver-tests
-
- clean install
-
-
- org.codehaus.groovy
- groovy-all
+ eu.ztsh.mockserver
+ mockserver
- org.spockframework
- spock-core
+ eu.ztsh.mockserver
+ mockserver-client
+
+
+ org.apache.groovy
+ groovy
+
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
org.slf4j
slf4j-api
ch.qos.logback
- logback-classic
+ logback-core
+
- org.apache.httpcomponents
- httpclient
-
-
- pl.touk.mockserver
- mockserver
- ${project.version}
-
-
- pl.touk.mockserver
- mockserver-client
- ${project.version}
+ org.spockframework
+ spock-core
+
+
+
+ org.codehaus.gmavenplus
+ gmavenplus-plugin
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.2.5
+
+
+ **/*Test.java
+
+
+
+
+
+
diff --git a/mockserver-tests/src/test/groovy/eu/ztsh/mockserver/tests/MockServerHttpsTest.groovy b/mockserver-tests/src/test/groovy/eu/ztsh/mockserver/tests/MockServerHttpsTest.groovy
new file mode 100644
index 0000000..e1ecd95
--- /dev/null
+++ b/mockserver-tests/src/test/groovy/eu/ztsh/mockserver/tests/MockServerHttpsTest.groovy
@@ -0,0 +1,156 @@
+package eu.ztsh.mockserver.tests
+
+import eu.ztsh.mockserver.api.common.Https
+import eu.ztsh.mockserver.api.request.AddMock
+import eu.ztsh.mockserver.client.RemoteMockServer
+import eu.ztsh.mockserver.client.Util
+import eu.ztsh.mockserver.server.HttpMockServer
+import groovy.xml.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 spock.lang.AutoCleanup
+import spock.lang.Ignore
+import spock.lang.Shared
+import spock.lang.Specification
+
+import javax.net.ssl.SSLContext
+import javax.net.ssl.SSLHandshakeException
+import java.security.KeyStore
+
+@Ignore
+class MockServerHttpsTest extends Specification {
+
+ RemoteMockServer remoteMockServer = new RemoteMockServer('localhost', 19000)
+
+ @AutoCleanup('stop')
+ HttpMockServer httpMockServer = new HttpMockServer(19000)
+
+ @Shared
+ SSLContext noClientAuthSslContext = SSLContexts.custom()
+ .loadTrustMaterial(trustStore())
+ .build()
+
+ @Shared
+ SSLContext trustedCertificateSslContext = SSLContexts.custom()
+ .loadKeyMaterial(trustedCertificateKeystore(), 'changeit'.toCharArray())
+ .loadTrustMaterial(trustStore())
+ .build()
+
+ @Shared
+ SSLContext untrustedCertificateSslContext = SSLContexts.custom()
+ .loadKeyMaterial(untrustedCertificateKeystore(), 'changeit'.toCharArray())
+ .loadTrustMaterial(trustStore())
+ .build()
+
+ @Ignore("TODO: SSL peer shut down incorrectly")
+ def 'should handle HTTPS server' () {
+ given:
+ 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(noClientAuthSslContext).execute(restPost)
+ then:
+ GPathResult restPostResponse = Util.extractXmlResponse(response)
+ restPostResponse.name() == 'goodResponse-request'
+ }
+
+ @Ignore("TODO: SSL peer shut down incorrectly")
+ def 'should handle HTTPS server with client auth' () {
+ given:
+ 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,
+ truststorePath: MockServerHttpsTest.classLoader.getResource('truststore.jks').path,
+ truststorePassword: 'changeit',
+ requireClientAuth: true
+ ),
+ soap: false
+ ))
+ when:
+ HttpPost restPost = new HttpPost('https://localhost:10443/testEndpoint')
+ restPost.entity = new StringEntity('', ContentType.create("text/xml", "UTF-8"))
+ CloseableHttpResponse response = client(trustedCertificateSslContext).execute(restPost)
+ then:
+ GPathResult restPostResponse = Util.extractXmlResponse(response)
+ restPostResponse.name() == 'goodResponse-request'
+ }
+
+ def 'should handle HTTPS server with wrong client auth' () {
+ given:
+ 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,
+ truststorePath: MockServerHttpsTest.classLoader.getResource('truststore.jks').path,
+ truststorePassword: 'changeit',
+ requireClientAuth: true
+ ),
+ soap: false
+ ))
+ when:
+ HttpPost restPost = new HttpPost('https://localhost:10443/testEndpoint')
+ restPost.entity = new StringEntity('', ContentType.create("text/xml", "UTF-8"))
+ client(sslContext).execute(restPost)
+ then:
+ thrown(SSLHandshakeException)
+ where:
+ sslContext << [noClientAuthSslContext, untrustedCertificateSslContext]
+ }
+
+ private CloseableHttpClient client(SSLContext sslContext) {
+ return HttpClients.custom()
+ .setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
+ .setSslcontext(sslContext)
+ .build()
+ }
+
+ private KeyStore trustedCertificateKeystore() {
+ return loadKeystore('trusted.jks')
+ }
+
+ private KeyStore untrustedCertificateKeystore() {
+ return loadKeystore('untrusted.jks')
+ }
+
+ private KeyStore trustStore() {
+ return loadKeystore('truststore.jks')
+ }
+
+ private KeyStore loadKeystore(String fileName) {
+ KeyStore truststore = KeyStore.getInstance(KeyStore.defaultType)
+ truststore.load(new FileInputStream(MockServerHttpsTest.classLoader.getResource(fileName).path), "changeit".toCharArray());
+ return truststore
+ }
+}
diff --git a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy b/mockserver-tests/src/test/groovy/eu/ztsh/mockserver/tests/MockServerIntegrationTest.groovy
similarity index 85%
rename from mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy
rename to mockserver-tests/src/test/groovy/eu/ztsh/mockserver/tests/MockServerIntegrationTest.groovy
index 1412de1..038a143 100644
--- a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy
+++ b/mockserver-tests/src/test/groovy/eu/ztsh/mockserver/tests/MockServerIntegrationTest.groovy
@@ -1,6 +1,6 @@
-package pl.touk.mockserver.tests
+package eu.ztsh.mockserver.tests
-import groovy.util.slurpersupport.GPathResult
+import groovy.xml.slurpersupport.GPathResult
import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.client.methods.HttpDelete
import org.apache.http.client.methods.HttpGet
@@ -15,29 +15,29 @@ import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.HttpClients
import org.apache.http.util.EntityUtils
-import pl.touk.mockserver.api.common.ImportAlias
-import pl.touk.mockserver.api.common.Method
-import pl.touk.mockserver.api.request.AddMock
-import pl.touk.mockserver.api.response.MockEventReport
-import pl.touk.mockserver.api.response.MockReport
-import pl.touk.mockserver.client.InvalidMockDefinition
-import pl.touk.mockserver.client.InvalidMockRequestSchema
-import pl.touk.mockserver.client.MockAlreadyExists
-import pl.touk.mockserver.client.MockDoesNotExist
-import pl.touk.mockserver.client.RemoteMockServer
-import pl.touk.mockserver.client.Util
-import pl.touk.mockserver.server.HttpMockServer
-import spock.lang.Shared
+import eu.ztsh.mockserver.api.common.ImportAlias
+import eu.ztsh.mockserver.api.common.Method
+import eu.ztsh.mockserver.api.request.AddMock
+import eu.ztsh.mockserver.api.response.MockEventReport
+import eu.ztsh.mockserver.api.response.MockReport
+import eu.ztsh.mockserver.client.InvalidMockDefinition
+import eu.ztsh.mockserver.client.InvalidMockRequestSchema
+import eu.ztsh.mockserver.client.MockAlreadyExists
+import eu.ztsh.mockserver.client.MockDoesNotExist
+import eu.ztsh.mockserver.client.RemoteMockServer
+import eu.ztsh.mockserver.client.Util
+import eu.ztsh.mockserver.server.HttpMockServer
+import spock.lang.AutoCleanup
+import spock.lang.Ignore
import spock.lang.Specification
-import spock.lang.Unroll
class MockServerIntegrationTest extends Specification {
RemoteMockServer remoteMockServer
+ @AutoCleanup('stop')
HttpMockServer httpMockServer
- @Shared
CloseableHttpClient client = HttpClients.createDefault()
def setup() {
@@ -45,10 +45,6 @@ class MockServerIntegrationTest extends Specification {
remoteMockServer = new RemoteMockServer('localhost', 9000)
}
- def cleanup() {
- httpMockServer.stop()
- }
-
def "should add working rest mock on endpoint"() {
expect:
remoteMockServer.addMock(new AddMock(
@@ -70,6 +66,7 @@ class MockServerIntegrationTest extends Specification {
remoteMockServer.removeMock('testRest')?.size() == 1
}
+ @Ignore("TODO: restPostResponse.name()")
def "should add working rest mock on endpoint with utf"() {
expect:
remoteMockServer.addMock(new AddMock(
@@ -244,7 +241,6 @@ class MockServerIntegrationTest extends Specification {
soapPostResponse.Body.'goodResponseSoap-request'.size() == 1
}
- @Unroll
def "should dispatch rest mocks when second on #name"() {
given:
remoteMockServer.addMock(new AddMock(
@@ -283,7 +279,6 @@ class MockServerIntegrationTest extends Specification {
9998 | 'test2' | 'another port and path'
}
- @Unroll
def "should dispatch rest mock with response code"() {
given:
remoteMockServer.addMock(new AddMock(
@@ -857,7 +852,6 @@ class MockServerIntegrationTest extends Specification {
mockEvents2[0].response.statusCode == 202
}
- @Unroll
def "should return mock report with #mockEvents events when deleting mock with flag skip mock = #skipReport"() {
expect:
remoteMockServer.addMock(new AddMock(
@@ -885,7 +879,6 @@ class MockServerIntegrationTest extends Specification {
true | 0
}
- @Unroll
def "should reject mock when it has System.exit in closure"() {
when:
remoteMockServer.addMock(new AddMock(
@@ -1029,4 +1022,162 @@ class MockServerIntegrationTest extends Specification {
expect:
remoteMockServer.removeMock('testRest')?.size() == 1
}
+
+ def "should get configuration of mocks and reconfigure new mock server based on it"() {
+ given:
+ remoteMockServer.addMock(new AddMock(
+ name: 'testRest2',
+ path: 'testEndpoint',
+ port: 9998,
+ predicate: '''{ req -> req.xml.name() == 'request1'}''',
+ response: '''{ req -> '' }''',
+ responseHeaders: '{ _ -> [a: "b"] }'
+ ))
+ remoteMockServer.addMock(new AddMock(
+ name: 'testRest4',
+ path: 'testEndpoint',
+ port: 9999,
+ soap: true,
+ statusCode: 204,
+ method: Method.PUT
+ ))
+ remoteMockServer.addMock(new AddMock(
+ name: 'testRest3',
+ path: 'testEndpoint2',
+ port: 9999
+ ))
+ remoteMockServer.addMock(new AddMock(
+ name: 'testRest5',
+ path: 'testEndpoint',
+ port: 9999
+ ))
+ remoteMockServer.addMock(new AddMock(
+ name: 'testRest6',
+ path: 'testEndpoint2',
+ port: 9999
+ ))
+ remoteMockServer.addMock(new AddMock(
+ name: 'testRest',
+ path: 'testEndpoint',
+ port: 9999,
+ schema: 'schema2.xsd',
+ imports: [
+ new ImportAlias(alias: 'aaa', fullClassName: 'bbb'),
+ new ImportAlias(alias: 'ccc', fullClassName: 'bla')
+ ],
+ preserveHistory: true
+ ))
+ remoteMockServer.removeMock('testRest5')
+ when:
+ ConfigObject configObject = remoteMockServer.configuration
+ httpMockServer.stop()
+ httpMockServer = new HttpMockServer(9000, configObject)
+
+ then:
+ List mockReport = remoteMockServer.listMocks()
+ mockReport.size() == 5
+ assertMockReport(mockReport[0], [name: 'testRest', path: 'testEndpoint', port: 9999, predicate: '{ _ -> true }', response: '''{ _ -> '' }''', responseHeaders: '{ _ -> [:] }', soap: false, statusCode: 200, method: Method.POST, schema: 'schema2.xsd', preserveHistory: true])
+ assertMockReport(mockReport[1], [name: 'testRest2', path: 'testEndpoint', port: 9998, predicate: '''{ req -> req.xml.name() == 'request1'}''', response: '''{ req -> '' }''', responseHeaders: '{ _ -> [a: "b"] }', soap: false, statusCode: 200, method: Method.POST])
+ assertMockReport(mockReport[2], [name: 'testRest3', path: 'testEndpoint2', port: 9999, predicate: '{ _ -> true }', response: '''{ _ -> '' }''', responseHeaders: '{ _ -> [:] }', soap: false, statusCode: 200, method: Method.POST])
+ assertMockReport(mockReport[3], [name: 'testRest4', path: 'testEndpoint', port: 9999, predicate: '{ _ -> true }', response: '''{ _ -> '' }''', responseHeaders: '{ _ -> [:] }', soap: true, statusCode: 204, method: Method.PUT])
+ assertMockReport(mockReport[4], [name: 'testRest6', path: 'testEndpoint2', port: 9999, predicate: '{ _ -> true }', response: '''{ _ -> '' }''', responseHeaders: '{ _ -> [:] }', soap: false, statusCode: 200, method: Method.POST])
+ mockReport[0].imports.find { it.alias == 'aaa' }?.fullClassName == 'bbb'
+ mockReport[0].imports.find { it.alias == 'ccc' }?.fullClassName == 'bla'
+ }
+
+ def "should add mock without history"() {
+ expect:
+ remoteMockServer.addMock(new AddMock(
+ name: 'testRest',
+ path: 'testEndpoint',
+ port: 9999,
+ predicate: '''{req -> req.xml.name() == 'request'}''',
+ response: '''{req -> ""}''',
+ soap: false,
+ preserveHistory: false
+ ))
+ when:
+ HttpPost restPost = new HttpPost('http://localhost:9999/testEndpoint')
+ restPost.entity = new StringEntity('', ContentType.create("text/xml", "UTF-8"))
+ CloseableHttpResponse response = client.execute(restPost)
+ then:
+ GPathResult restPostResponse = Util.extractXmlResponse(response)
+ restPostResponse.name() == 'goodResponseRest-request'
+ expect:
+ remoteMockServer.removeMock('testRest')?.size() == 0
+ }
+
+ def "should handle empty post"() {
+ expect:
+ remoteMockServer.addMock(new AddMock(
+ name: 'testRest',
+ path: 'testEndpoint',
+ port: 9999,
+ statusCode: 201,
+ soap: false
+ ))
+ when:
+ HttpPost restPost = new HttpPost('http://localhost:9999/testEndpoint')
+ CloseableHttpResponse response = client.execute(restPost)
+ then:
+ response.statusLine.statusCode == 201
+ Util.consumeResponse(response)
+ expect:
+ remoteMockServer.removeMock('testRest')?.size() == 1
+ }
+
+ def 'should handle leading slash'() {
+ given:
+ String name = "testRest-${UUID.randomUUID().toString()}"
+ expect:
+ remoteMockServer.addMock(new AddMock(
+ name: name,
+ path: mockPath,
+ port: 9999,
+ statusCode: 201,
+ soap: false
+ ))
+ when:
+ HttpPost restPost = new HttpPost("http://localhost:9999/$urlPath")
+ CloseableHttpResponse response = client.execute(restPost)
+ then:
+ response.statusLine.statusCode == 201
+ Util.consumeResponse(response)
+ expect:
+ remoteMockServer.removeMock(name)?.size() == 1
+ where:
+ mockPath | urlPath
+ '' | ''
+ '/' | ''
+ 'test' | 'test'
+ '/test' | 'test'
+ 'test/other' | 'test/other'
+ '/test/other' | 'test/other'
+ }
+
+ def 'should match any method'() {
+ given:
+ String name = "testRest-${UUID.randomUUID().toString()}"
+ remoteMockServer.addMock(new AddMock(
+ name: name,
+ path: 'any-method',
+ port: 9999,
+ statusCode: 201,
+ soap: false,
+ method: Method.ANY_METHOD
+ ))
+ when:
+ CloseableHttpResponse response = client.execute(req)
+ then:
+ response.statusLine.statusCode == 201
+ Util.consumeResponse(response)
+ cleanup:
+ remoteMockServer.removeMock(name)
+ where:
+ req << [
+ new HttpGet('http://localhost:9999/any-method'),
+ new HttpPost('http://localhost:9999/any-method'),
+ new HttpPatch('http://localhost:9999/any-method')
+ ]
+ }
}
diff --git a/mockserver-tests/src/test/groovy/eu/ztsh/mockserver/tests/MockServerMaxUsesTest.groovy b/mockserver-tests/src/test/groovy/eu/ztsh/mockserver/tests/MockServerMaxUsesTest.groovy
new file mode 100644
index 0000000..eda365c
--- /dev/null
+++ b/mockserver-tests/src/test/groovy/eu/ztsh/mockserver/tests/MockServerMaxUsesTest.groovy
@@ -0,0 +1,239 @@
+package eu.ztsh.mockserver.tests
+
+
+import org.apache.http.client.methods.CloseableHttpResponse
+import org.apache.http.client.methods.HttpPost
+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 eu.ztsh.mockserver.api.request.AddMock
+import eu.ztsh.mockserver.client.RemoteMockServer
+import eu.ztsh.mockserver.server.HttpMockServer
+import spock.lang.AutoCleanup
+import spock.lang.Specification
+
+class MockServerMaxUsesTest extends Specification {
+
+ RemoteMockServer remoteMockServer
+
+ @AutoCleanup('stop')
+ HttpMockServer httpMockServer
+
+ CloseableHttpClient client = HttpClients.createDefault()
+
+ def setup() {
+ httpMockServer = new HttpMockServer(9000)
+ remoteMockServer = new RemoteMockServer('localhost', 9000)
+ }
+
+ def 'should return two mocks in order'() {
+ given:'mock with predicate is given but for only one use'
+ remoteMockServer.addMock(new AddMock(
+ name: 'mock1',
+ path: 'testEndpoint',
+ port: 9999,
+ predicate: '''{req -> req.xml.name() == 'request'}''',
+ response: '''{req -> 'mock1'}''',
+ maxUses: 1
+ ))
+ and:'mock with the same predicate is given'
+ remoteMockServer.addMock(new AddMock(
+ name: 'mock2',
+ path: 'testEndpoint',
+ port: 9999,
+ predicate: '''{req -> req.xml.name() == 'request'}''',
+ response: '''{req -> 'mock2'}''',
+ ))
+ when:'we call the first time'
+ HttpPost restPost = new HttpPost('http://localhost:9999/testEndpoint')
+ restPost.entity = new StringEntity('', ContentType.create("text/xml", "UTF-8"))
+ CloseableHttpResponse response = client.execute(restPost)
+ then:'first mock should be returned and expired'
+ response.entity.content.text == 'mock1'
+ when:'we call the second time using the same request'
+ CloseableHttpResponse response2 = client.execute(restPost)
+ then:'second mock should be returned'
+ response2.entity.content.text == 'mock2'
+ when:'we call the third time using the same request'
+ CloseableHttpResponse response3 = client.execute(restPost)
+ then:'second mock should be returned, because it has unlimited uses'
+ response3.entity.content.text == 'mock2'
+ }
+
+ def 'should return two mocks in order but only once'() {
+ given:'mock with predicate is given but for only one use'
+ remoteMockServer.addMock(new AddMock(
+ name: 'mock1',
+ path: 'testEndpoint',
+ port: 9999,
+ predicate: '''{req -> req.xml.name() == 'request'}''',
+ response: '''{req -> 'mock1'}''',
+ maxUses: 1
+ ))
+ and:'mock with the same predicate is given'
+ remoteMockServer.addMock(new AddMock(
+ name: 'mock2',
+ path: 'testEndpoint',
+ port: 9999,
+ predicate: '''{req -> req.xml.name() == 'request'}''',
+ response: '''{req -> 'mock2'}''',
+ maxUses: 1,
+ ))
+ when:'we call the first time'
+ HttpPost restPost = new HttpPost('http://localhost:9999/testEndpoint')
+ restPost.entity = new StringEntity('', ContentType.create("text/xml", "UTF-8"))
+ CloseableHttpResponse response = client.execute(restPost)
+ then:'first mock should be returned and expired'
+ response.entity.content.text == 'mock1'
+ when:'we call the second time using the same request'
+ CloseableHttpResponse response2 = client.execute(restPost)
+ then:'second mock should be returned'
+ response2.entity.content.text == 'mock2'
+ when:'we call the third time using the same request'
+ CloseableHttpResponse response3 = client.execute(restPost)
+ then:'no mock should be found'
+ response3.statusLine.statusCode == 404
+ and:'mock should exist'
+ remoteMockServer.listMocks().find { it.name == 'mock1' } != null
+ }
+
+ def 'should return two mocks in cyclic order'() {
+ given:'mock with predicate is given but for only one use'
+ remoteMockServer.addMock(new AddMock(
+ name: 'mock1',
+ path: 'testEndpoint',
+ port: 9999,
+ predicate: '''{req -> req.xml.name() == 'request'}''',
+ response: '''{req -> 'mock1'}''',
+ maxUses: 1,
+ cyclic: true,
+ preserveHistory: true
+ ))
+ and:'mock with the same predicate is given'
+ remoteMockServer.addMock(new AddMock(
+ name: 'mock2',
+ path: 'testEndpoint',
+ port: 9999,
+ predicate: '''{req -> req.xml.name() == 'request'}''',
+ response: '''{req -> 'mock2'}''',
+ maxUses: 1,
+ cyclic: true
+ ))
+ when:'we call the first time'
+ HttpPost restPost = new HttpPost('http://localhost:9999/testEndpoint')
+ restPost.entity = new StringEntity('', ContentType.create("text/xml", "UTF-8"))
+ CloseableHttpResponse response = client.execute(restPost)
+ then:'first mock should be returned and expired'
+ response.entity.content.text == 'mock1'
+ when:'we call the second time using the same request'
+ CloseableHttpResponse response2 = client.execute(restPost)
+ then:'second mock should be returned and expired'
+ response2.entity.content.text == 'mock2'
+ when:'we call the third time using the same request'
+ CloseableHttpResponse response3 = client.execute(restPost)
+ then:'first mock should be returned, because these mocks are cyclic'
+ response3.entity.content.text == 'mock1'
+ when:'we call the fourth time using the same request'
+ CloseableHttpResponse response4 = client.execute(restPost)
+ then:'second mock should be returned, because these mocks are cyclic'
+ response4.entity.content.text == 'mock2'
+ and:
+ remoteMockServer.peekMock('mock1').size() == 2
+ }
+
+ def 'should return two mocks with the same request interjected by another'() {
+ given:'mock with predicate is given but for only one use'
+ remoteMockServer.addMock(new AddMock(
+ name: 'mock1',
+ path: 'testEndpoint',
+ port: 9999,
+ predicate: '''{req -> req.xml.name() == 'request'}''',
+ response: '''{req -> 'mock1'}''',
+ maxUses: 1,
+ cyclic: true
+ ))
+ and:'mock with the same predicate is given'
+ remoteMockServer.addMock(new AddMock(
+ name: 'mock2',
+ path: 'testEndpoint',
+ port: 9999,
+ predicate: '''{req -> req.xml.name() == 'request'}''',
+ response: '''{req -> 'mock2'}''',
+ maxUses: 1,
+ cyclic: true
+ ))
+ and:'mock with other predicate is given'
+ remoteMockServer.addMock(new AddMock(
+ name: 'otherMock',
+ path: 'testEndpoint',
+ port: 9999,
+ predicate: '''{req -> req.xml.name() == 'otherRequest'}''',
+ response: '''{req -> 'otherMock'}'''
+ ))
+ when:'we call the first time'
+ HttpPost restPost = new HttpPost('http://localhost:9999/testEndpoint')
+ restPost.entity = new StringEntity('', ContentType.create("text/xml", "UTF-8"))
+ CloseableHttpResponse response = client.execute(restPost)
+ then:'first mock should be returned and expired'
+ response.entity.content.text == 'mock1'
+ when:'we call other request'
+ HttpPost otherRestPost = new HttpPost('http://localhost:9999/testEndpoint')
+ otherRestPost.entity = new StringEntity('', ContentType.create("text/xml", "UTF-8"))
+ CloseableHttpResponse otherResponse = client.execute(otherRestPost)
+ then:'other mock should be called'
+ otherResponse.entity.content.text == 'otherMock'
+ when:'we call the second time using the same request'
+ CloseableHttpResponse response2 = client.execute(restPost)
+ then:'second mock should be returned and expired'
+ response2.entity.content.text == 'mock2'
+ when:'we call the third time using the same request'
+ CloseableHttpResponse response3 = client.execute(restPost)
+ then:'first mock should be returned, because these mocks are cyclic'
+ response3.entity.content.text == 'mock1'
+ }
+
+ def 'should return first mock twice'() {
+ given:'mock with predicate is given but for only one use'
+ remoteMockServer.addMock(new AddMock(
+ name: 'mock1',
+ path: 'testEndpoint',
+ port: 9999,
+ predicate: '''{req -> req.xml.name() == 'request'}''',
+ response: '''{req -> 'mock1'}''',
+ maxUses: 2
+ ))
+ and:'mock with the same predicate is given'
+ remoteMockServer.addMock(new AddMock(
+ name: 'mock2',
+ path: 'testEndpoint',
+ port: 9999,
+ predicate: '''{req -> req.xml.name() == 'request'}''',
+ response: '''{req -> 'mock2'}''',
+ ))
+ when:'we call the first time'
+ HttpPost restPost = new HttpPost('http://localhost:9999/testEndpoint')
+ restPost.entity = new StringEntity('', ContentType.create("text/xml", "UTF-8"))
+ CloseableHttpResponse response = client.execute(restPost)
+ then:'first mock should be returned and expired'
+ response.entity.content.text == 'mock1'
+ when:'we call the second time using the same request'
+ CloseableHttpResponse response2 = client.execute(restPost)
+ then:'again first mock should be returned'
+ response2.entity.content.text == 'mock1'
+ when:'we call the third time using the same request'
+ CloseableHttpResponse response3 = client.execute(restPost)
+ then:'second mock should be returned'
+ response3.entity.content.text == 'mock2'
+ }
+
+ def 'should throw exception if adding mock with incorrect maxUses'() {
+ when:
+ remoteMockServer.addMock(new AddMock(
+ name: 'mock1',
+ maxUses: 0
+ ))
+ then:
+ thrown(RuntimeException)
+ }
+}
diff --git a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/ServerMockPT.groovy b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/ServerMockPT.groovy
deleted file mode 100644
index a696a98..0000000
--- a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/ServerMockPT.groovy
+++ /dev/null
@@ -1,59 +0,0 @@
-package pl.touk.mockserver.tests
-
-import org.apache.http.client.HttpClient
-import org.apache.http.client.methods.CloseableHttpResponse
-import org.apache.http.client.methods.HttpPost
-import org.apache.http.entity.ContentType
-import org.apache.http.entity.StringEntity
-import org.apache.http.impl.client.HttpClients
-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.Specification
-import spock.lang.Timeout
-
-import java.util.concurrent.ExecutorService
-import java.util.concurrent.Executors
-import java.util.concurrent.TimeUnit
-
-class ServerMockPT extends Specification {
-
- @Timeout(value = 90)
- def "should handle many request simultaneously"() {
- given:
- HttpClient client = HttpClients.createDefault()
- HttpMockServer httpMockServer = new HttpMockServer()
- RemoteMockServer controlServerClient = new RemoteMockServer("localhost", 9999)
- int requestAmount = 1000
- String[] responses = new String[requestAmount]
- ExecutorService executorService = Executors.newCachedThreadPool()
- when:
- for (int i = 0; i < requestAmount; ++i) {
- int current = i
- executorService.submit {
- int endpointNumber = current % 10
- int port = 9000 + (current % 7)
- controlServerClient.addMock(new AddMock(
- name: "testRest$current",
- path: "testEndpoint$endpointNumber",
- port: port,
- predicate: """{req -> req.xml.name() == 'request$current'}""",
- response: """{req -> ""}"""
- ))
- HttpPost restPost = new HttpPost("http://localhost:$port/testEndpoint$endpointNumber")
- restPost.entity = new StringEntity("", ContentType.create("text/xml", "UTF-8"))
- CloseableHttpResponse response = client.execute(restPost)
- responses[current] = Util.extractStringResponse(response)
- controlServerClient.removeMock("testRest$current", true)
- }
- }
- executorService.shutdown()
- executorService.awaitTermination(90, TimeUnit.SECONDS)
- then:
- responses.eachWithIndex { res, i -> assert res && new XmlSlurper().parseText(res).name() == "goodResponse$i" as String }
- cleanup:
- executorService.shutdown()
- httpMockServer.stop()
- }
-}
diff --git a/mockserver-tests/src/test/resources/keystore.jks b/mockserver-tests/src/test/resources/keystore.jks
new file mode 100644
index 0000000..d5e35d1
Binary files /dev/null and b/mockserver-tests/src/test/resources/keystore.jks differ
diff --git a/mockserver-tests/src/test/resources/trusted.jks b/mockserver-tests/src/test/resources/trusted.jks
new file mode 100644
index 0000000..e6fa704
Binary files /dev/null and b/mockserver-tests/src/test/resources/trusted.jks differ
diff --git a/mockserver-tests/src/test/resources/truststore.jks b/mockserver-tests/src/test/resources/truststore.jks
new file mode 100644
index 0000000..27a8332
Binary files /dev/null and b/mockserver-tests/src/test/resources/truststore.jks differ
diff --git a/mockserver-tests/src/test/resources/untrusted.jks b/mockserver-tests/src/test/resources/untrusted.jks
new file mode 100644
index 0000000..ca94b45
Binary files /dev/null and b/mockserver-tests/src/test/resources/untrusted.jks differ
diff --git a/mockserver/pom.xml b/mockserver/pom.xml
index 967e9f5..d097048 100644
--- a/mockserver/pom.xml
+++ b/mockserver/pom.xml
@@ -1,46 +1,76 @@
-
-
- http-mock-server
- pl.touk.mockserver
- 2.2.0
-
+
4.0.0
+
+ eu.ztsh.mockserver
+ http-mock-server
+ 3.0.0-SNAPSHOT
+
+
mockserver
- org.codehaus.groovy
- groovy-all
+ eu.ztsh.mockserver
+ mockserver-api
+
+
+ org.apache.groovy
+ groovy
+
+
+ org.apache.groovy
+ groovy-json
+
+
+ org.apache.groovy
+ groovy-xml
+
+
+
+ org.glassfish.jaxb
+ jaxb-core
+
+
+ org.glassfish.jaxb
+ jaxb-runtime
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
org.slf4j
slf4j-api
+
+ ch.qos.logback
+ logback-core
+
ch.qos.logback
logback-classic
-
- org.apache.commons
- commons-lang3
-
-
- pl.touk.mockserver
- mockserver-api
-
clean package assembly:single install
+
+ org.codehaus.gmavenplus
+ gmavenplus-plugin
+
maven-assembly-plugin
- pl.touk.mockserver.server.Main
+ eu.ztsh.mockserver.server.Main
@@ -49,7 +79,17 @@
mockserver-full
false
+
+
+ create-archive
+ package
+
+ single
+
+
+
+
diff --git a/mockserver/src/main/groovy/eu/ztsh/mockserver/server/ContextExecutor.groovy b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/ContextExecutor.groovy
new file mode 100644
index 0000000..6023a1c
--- /dev/null
+++ b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/ContextExecutor.groovy
@@ -0,0 +1,112 @@
+package eu.ztsh.mockserver.server
+
+import com.sun.net.httpserver.HttpExchange
+import groovy.transform.PackageScope
+import groovy.util.logging.Slf4j
+import eu.ztsh.mockserver.api.common.Method
+
+import java.util.concurrent.CopyOnWriteArrayList
+
+@Slf4j
+@PackageScope
+class ContextExecutor {
+ private final HttpServerWrapper httpServerWrapper
+ final String path
+ private final List mocks
+
+ ContextExecutor(HttpServerWrapper httpServerWrapper, Mock initialMock) {
+ this.httpServerWrapper = httpServerWrapper
+ this.path = "/${initialMock.path}"
+ this.mocks = new CopyOnWriteArrayList<>([initialMock])
+ httpServerWrapper.createContext(path) {
+ HttpExchange ex ->
+ try {
+ applyMocks(ex)
+ } catch (Exception e) {
+ log.error("Exceptiony occured handling request", e)
+ throw e
+ } finally {
+ ex.close()
+ }
+ }
+ }
+
+ private void applyMocks(HttpExchange ex) {
+ MockRequest request = new MockRequest(ex.requestBody.text, ex.requestHeaders, ex.requestURI)
+ log.info('Mock received input')
+ log.debug("Request: ${request.text}")
+ for (Mock mock : mocks) {
+ try {
+ if (mock.match(Method.valueOf(ex.requestMethod), request)) {
+ log.debug("Mock ${mock.name} match request ${request.text}")
+ handleMaxUses(mock)
+ MockResponse httpResponse = mock.apply(request)
+ fillExchange(ex, httpResponse)
+ log.trace("Mock ${mock.name} response with body ${httpResponse.text}")
+ return
+ }
+ log.debug("Mock ${mock.name} does not match request")
+ } catch (Exception e) {
+ log.warn("An exception occured when matching or applying mock ${mock.name}", e)
+ }
+ }
+ log.warn("Any mock does not match request ${request.text}")
+ Util.createResponse(ex, request.text, 404)
+ }
+
+ String getPath() {
+ return path.substring(1)
+ }
+
+ String getContextPath() {
+ return path
+ }
+
+ private static void fillExchange(HttpExchange httpExchange, MockResponse response) {
+ response.headers.each {
+ httpExchange.responseHeaders.add(it.key, it.value)
+ }
+ Util.createResponse(httpExchange, response.text, response.statusCode)
+ }
+
+ List removeMock(String name) {
+ Mock mock = mocks.find { it.name == name }
+ if (mock) {
+ mocks.remove(mock)
+ return mock.history
+ }
+ return []
+ }
+
+ List peekMock(String name) {
+ Mock mock = mocks.find { it.name == name }
+ if (mock) {
+ return mock.history
+ }
+ return []
+ }
+
+ void addMock(Mock mock) {
+ mocks << mock
+ }
+
+ List getMocks() {
+ return mocks
+ }
+
+ private synchronized void handleMaxUses(Mock mock) {
+ if (mock.hasLimitedUses()) {
+ mock.decrementUses()
+ resetIfNeeded(mock)
+ log.debug("Uses left ${mock.usesLeft} of ${mock.maxUses} (is cyclic: ${mock.cyclic})")
+ }
+ }
+
+ private void resetIfNeeded(Mock mock) {
+ if (mock.shouldUsesBeReset()) {
+ mock.resetUses()
+ mocks.remove(mock)
+ mocks.add(mock)
+ }
+ }
+}
diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/HttpMockServer.groovy
similarity index 54%
rename from mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy
rename to mockserver/src/main/groovy/eu/ztsh/mockserver/server/HttpMockServer.groovy
index c25b990..eaae94d 100644
--- a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy
+++ b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/HttpMockServer.groovy
@@ -1,47 +1,62 @@
-package pl.touk.mockserver.server
+package eu.ztsh.mockserver.server
import com.sun.net.httpserver.HttpExchange
import groovy.util.logging.Slf4j
-import pl.touk.mockserver.api.common.ImportAlias
-import pl.touk.mockserver.api.request.AddMock
-import pl.touk.mockserver.api.request.MockServerRequest
-import pl.touk.mockserver.api.request.PeekMock
-import pl.touk.mockserver.api.request.RemoveMock
-import pl.touk.mockserver.api.response.ExceptionOccured
-import pl.touk.mockserver.api.response.MockAdded
-import pl.touk.mockserver.api.response.MockEventReport
-import pl.touk.mockserver.api.response.MockPeeked
-import pl.touk.mockserver.api.response.MockRemoved
-import pl.touk.mockserver.api.response.MockReport
-import pl.touk.mockserver.api.response.MockRequestReport
-import pl.touk.mockserver.api.response.MockResponseReport
-import pl.touk.mockserver.api.response.Mocks
-import pl.touk.mockserver.api.response.Parameter
+import eu.ztsh.mockserver.api.common.Https
+import eu.ztsh.mockserver.api.common.ImportAlias
+import eu.ztsh.mockserver.api.common.Method
+import eu.ztsh.mockserver.api.request.AddMock
+import eu.ztsh.mockserver.api.request.MockServerRequest
+import eu.ztsh.mockserver.api.request.PeekMock
+import eu.ztsh.mockserver.api.request.RemoveMock
+import eu.ztsh.mockserver.api.response.ExceptionOccured
+import eu.ztsh.mockserver.api.response.MockAdded
+import eu.ztsh.mockserver.api.response.MockEventReport
+import eu.ztsh.mockserver.api.response.MockPeeked
+import eu.ztsh.mockserver.api.response.MockRemoved
+import eu.ztsh.mockserver.api.response.MockReport
+import eu.ztsh.mockserver.api.response.MockRequestReport
+import eu.ztsh.mockserver.api.response.MockResponseReport
+import eu.ztsh.mockserver.api.response.Mocks
+import eu.ztsh.mockserver.api.response.Parameter
-import javax.xml.bind.JAXBContext
+import jakarta.xml.bind.JAXBContext
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArraySet
+import java.util.concurrent.Executor
+import java.util.concurrent.Executors
-import static pl.touk.mockserver.server.Util.createResponse
+import static eu.ztsh.mockserver.server.Util.createResponse
@Slf4j
class HttpMockServer {
- private final HttpServerWraper httpServerWraper
- private final Map childServers = new ConcurrentHashMap<>()
+ private final HttpServerWrapper httpServerWrapper
+ private final Map childServers = new ConcurrentHashMap<>()
private final Set mockNames = new CopyOnWriteArraySet<>()
+ private final ConfigObject configuration = new ConfigObject()
+ private final Executor executor
private static
final JAXBContext requestJaxbContext = JAXBContext.newInstance(AddMock.package.name, AddMock.classLoader)
- HttpMockServer(int port = 9999) {
- httpServerWraper = new HttpServerWraper(port)
+ HttpMockServer(int port = 9999, ConfigObject initialConfiguration = new ConfigObject(), int threads = 10) {
+ executor = Executors.newFixedThreadPool(threads)
+ httpServerWrapper = new HttpServerWrapper(port, executor)
- httpServerWraper.createContext('/serverControl', {
+ initialConfiguration.values()?.each { ConfigObject co ->
+ addMock(co)
+ }
+
+ httpServerWrapper.createContext('/serverControl', {
HttpExchange ex ->
try {
if (ex.requestMethod == 'GET') {
- listMocks(ex)
+ if (ex.requestURI.path == '/serverControl/configuration') {
+ createResponse(ex, configuration.prettyPrint(), 200)
+ } else {
+ listMocks(ex)
+ }
} else if (ex.requestMethod == 'POST') {
MockServerRequest request = requestJaxbContext.createUnmarshaller().unmarshal(ex.requestBody) as MockServerRequest
if (request instanceof AddMock) {
@@ -76,7 +91,8 @@ class HttpMockServer {
method: it.method,
statusCode: it.statusCode as int,
schema: it.schema,
- imports: it.imports.collect { new ImportAlias(alias: it.key, fullClassName: it.value) }
+ imports: it.imports.collect { new ImportAlias(alias: it.key, fullClassName: it.value) },
+ preserveHistory: it.preserveHistory
)
}
)
@@ -92,13 +108,50 @@ class HttpMockServer {
if (name in mockNames) {
throw new RuntimeException('mock already registered')
}
+ if (request.maxUses == 0) {
+ throw new RuntimeException('cannot set maxUses to 0')
+ }
Mock mock = mockFromRequest(request)
- HttpServerWraper child = getOrCreateChildServer(mock.port)
+ HttpServerWrapper child = getOrCreateChildServer(mock.port, mock.https)
child.addMock(mock)
+ saveConfiguration(request)
mockNames << name
createResponse(ex, new MockAdded(), 200)
}
+ private void addMock(ConfigObject co) {
+ String name = co.name
+ if (name in mockNames) {
+ throw new RuntimeException('mock already registered')
+ }
+ if (co.maxUses == 0) {
+ throw new RuntimeException('cannot set maxUses to 0')
+ }
+ Mock mock = mockFromConfig(co)
+ HttpServerWrapper child = getOrCreateChildServer(mock.port, mock.https)
+ child.addMock(mock)
+ configuration.put(name, co)
+ mockNames << name
+ }
+
+ private void saveConfiguration(AddMock request) {
+ ConfigObject mockDefinition = new ConfigObject()
+ request.metaPropertyValues.findAll { it.name != 'class' && it.value }.each {
+ if (it.name == 'imports') {
+ ConfigObject configObject = new ConfigObject()
+ it.value.each { ImportAlias imp ->
+ configObject.put(imp.alias, imp.fullClassName)
+ }
+ mockDefinition.put(it.name, configObject)
+ } else if (it.name == 'method') {
+ mockDefinition.put(it.name, it.value.name())
+ } else {
+ mockDefinition.put(it.name, it.value)
+ }
+ }
+ configuration.put(request.name, mockDefinition)
+ }
+
private static Mock mockFromRequest(AddMock request) {
Mock mock = new Mock(request.name, request.path, request.port)
mock.imports = request.imports?.collectEntries { [(it.alias): it.fullClassName] } ?: [:]
@@ -109,13 +162,43 @@ class HttpMockServer {
mock.method = request.method
mock.responseHeaders = request.responseHeaders
mock.schema = request.schema
+ mock.preserveHistory = request.preserveHistory != false
+ mock.https = request.https
+ mock.maxUses = request.maxUses
+ mock.cyclic = request.cyclic
return mock
}
- private HttpServerWraper getOrCreateChildServer(int mockPort) {
- HttpServerWraper child = childServers[mockPort]
+ private static Mock mockFromConfig(ConfigObject co) {
+ Mock mock = new Mock(co.name, co.path, co.port)
+ mock.imports = co.imports
+ mock.predicate = co.predicate ?: null
+ mock.response = co.response ?: null
+ mock.soap = co.soap ?: null
+ mock.statusCode = co.statusCode ?: null
+ mock.method = co.method ? Method.valueOf(co.method) : null
+ 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
+ )
+ }
+ mock.maxUses = co.maxUses ?: null
+ mock.cyclic = co.cyclic ?: null
+ return mock
+ }
+
+ private HttpServerWrapper getOrCreateChildServer(int mockPort, Https https) {
+ HttpServerWrapper child = childServers[mockPort]
if (!child) {
- child = new HttpServerWraper(mockPort)
+ child = new HttpServerWrapper(mockPort, executor, https)
childServers.put(mockPort, child)
}
return child
@@ -132,6 +215,7 @@ class HttpMockServer {
it.removeMock(name)
}.flatten() as List
mockNames.remove(name)
+ configuration.remove(name)
MockRemoved mockRemoved = new MockRemoved(
mockEvents: createMockEventReports(mockEvents)
)
@@ -182,6 +266,6 @@ class HttpMockServer {
void stop() {
childServers.values().each { it.stop() }
- httpServerWraper.stop()
+ httpServerWrapper.stop()
}
}
diff --git a/mockserver/src/main/groovy/eu/ztsh/mockserver/server/HttpServerWrapper.groovy b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/HttpServerWrapper.groovy
new file mode 100644
index 0000000..5d33156
--- /dev/null
+++ b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/HttpServerWrapper.groovy
@@ -0,0 +1,106 @@
+package eu.ztsh.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 eu.ztsh.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 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 removeMock(String name) {
+ return executors.collect { it.removeMock(name) }.flatten() as List
+ }
+
+ List peekMock(String name) {
+ return executors.collect { it.peekMock(name) }.flatten() as List
+ }
+
+ List getMocks() {
+ return executors.collect { it.mocks }.flatten() as List
+ }
+}
diff --git a/mockserver/src/main/groovy/eu/ztsh/mockserver/server/HttpsConfig.groovy b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/HttpsConfig.groovy
new file mode 100644
index 0000000..9d00714
--- /dev/null
+++ b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/HttpsConfig.groovy
@@ -0,0 +1,28 @@
+package eu.ztsh.mockserver.server
+
+import com.sun.net.httpserver.HttpsConfigurator
+import com.sun.net.httpserver.HttpsParameters
+import groovy.transform.CompileStatic
+import eu.ztsh.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
+ }
+}
diff --git a/mockserver/src/main/groovy/eu/ztsh/mockserver/server/Main.groovy b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/Main.groovy
new file mode 100644
index 0000000..916569e
--- /dev/null
+++ b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/Main.groovy
@@ -0,0 +1,33 @@
+package eu.ztsh.mockserver.server
+
+import groovy.util.logging.Slf4j
+
+@Slf4j
+class Main {
+ static void main(String[] args) {
+ HttpMockServer httpMockServer = startMockServer(args)
+
+ Runtime.runtime.addShutdownHook(new Thread({
+ log.info('Http server is stopping...')
+ httpMockServer.stop()
+ log.info('Http server is stopped')
+ } as Runnable))
+
+ while (true) {
+ Thread.sleep(10000)
+ }
+ }
+
+ private static HttpMockServer startMockServer(String... args) {
+ switch (args.length) {
+ case 1:
+ return new HttpMockServer(args[0] as int, new ConfigObject())
+ case 2:
+ return new HttpMockServer(args[0] as int, new ConfigSlurper().parse(new File(args[1]).toURI().toURL()))
+ case 3:
+ return new HttpMockServer(args[0] as int, new ConfigSlurper().parse(new File(args[1]).toURI().toURL()), args[2] as int)
+ default:
+ return new HttpMockServer()
+ }
+ }
+}
diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/Mock.groovy b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/Mock.groovy
similarity index 75%
rename from mockserver/src/main/groovy/pl/touk/mockserver/server/Mock.groovy
rename to mockserver/src/main/groovy/eu/ztsh/mockserver/server/Mock.groovy
index 6511078..821471c 100644
--- a/mockserver/src/main/groovy/pl/touk/mockserver/server/Mock.groovy
+++ b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/Mock.groovy
@@ -1,11 +1,12 @@
-package pl.touk.mockserver.server
+package eu.ztsh.mockserver.server
import groovy.transform.EqualsAndHashCode
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.Method
+import eu.ztsh.mockserver.api.common.Https
+import eu.ztsh.mockserver.api.common.Method
import javax.xml.XMLConstants
import javax.xml.transform.stream.StreamSource
@@ -34,18 +35,32 @@ class Mock implements Comparable {
String schema
private Validator validator
Map imports = [:]
+ boolean preserveHistory = true
+ Https https
+ int maxUses = -1
+ int usesLeft
+ boolean cyclic
Mock(String name, String path, int port) {
if (!(name)) {
throw new RuntimeException("Mock name must be given")
}
this.name = name
- this.path = path
+ this.path = stripLeadingSlash(path)
this.port = port
}
+ private static String stripLeadingSlash(String path) {
+ if (path?.startsWith('/')) {
+ return path - '/'
+ } else {
+ return path
+ }
+ }
+
boolean match(Method method, MockRequest request) {
- return this.method == method && predicate(request)
+ boolean usesCondition = hasLimitedUses() ? usesLeft > 0 : true
+ return usesCondition && (this.method == method || this.method == Method.ANY_METHOD) && predicate(request)
}
MockResponse apply(MockRequest request) {
@@ -60,7 +75,9 @@ class Mock implements Comparable {
}
} catch (Exception e) {
MockResponse response = new MockResponse(400, e.message, [:])
- history << new MockEvent(request, response)
+ if(preserveHistory) {
+ history << new MockEvent(request, response)
+ }
return response
}
}
@@ -69,7 +86,9 @@ class Mock implements Comparable {
String response = soap ? wrapSoap(responseText) : responseText
Map headers = responseHeaders(request)
MockResponse mockResponse = new MockResponse(statusCode, response, headers)
- history << new MockEvent(request, mockResponse)
+ if(preserveHistory) {
+ history << new MockEvent(request, mockResponse)
+ }
return mockResponse
}
@@ -98,7 +117,9 @@ class Mock implements Comparable {
}
compilerConfiguration.addCompilationCustomizers(customizer)
GroovyShell sh = new GroovyShell(this.class.classLoader, compilerConfiguration);
- return sh.evaluate(predicate) as Closure
+ Closure closure = sh.evaluate(predicate) as Closure
+ sh.resetLoadedClasses()
+ return closure
}
void setResponse(String response) {
@@ -131,6 +152,17 @@ class Mock implements Comparable {
}
}
+ void setMaxUses(Integer maxUses) {
+ if (maxUses > 0) {
+ this.maxUses = maxUses
+ this.usesLeft = maxUses
+ }
+ }
+
+ void setCyclic(Boolean cyclic) {
+ this.cyclic = cyclic ?: false
+ }
+
@Override
int compareTo(Mock o) {
return name.compareTo(o.name)
@@ -141,11 +173,27 @@ class Mock implements Comparable {
if (schema) {
try {
validator = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
- .newSchema(new File(this.class.getResource("/$schema").path))
+ .newSchema(this.class.getResource("/$schema"))
.newValidator()
} catch (Exception e) {
throw new RuntimeException('mock request schema is invalid schema', e)
}
}
}
+
+ boolean hasLimitedUses() {
+ return maxUses > 0
+ }
+
+ void decrementUses() {
+ usesLeft--
+ }
+
+ boolean shouldUsesBeReset() {
+ return hasLimitedUses() && usesLeft <= 0 && cyclic
+ }
+
+ void resetUses() {
+ setMaxUses(maxUses)
+ }
}
diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/MockEvent.groovy b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/MockEvent.groovy
similarity index 88%
rename from mockserver/src/main/groovy/pl/touk/mockserver/server/MockEvent.groovy
rename to mockserver/src/main/groovy/eu/ztsh/mockserver/server/MockEvent.groovy
index 97a46a2..9c429f1 100644
--- a/mockserver/src/main/groovy/pl/touk/mockserver/server/MockEvent.groovy
+++ b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/MockEvent.groovy
@@ -1,4 +1,4 @@
-package pl.touk.mockserver.server
+package eu.ztsh.mockserver.server
import groovy.transform.PackageScope
diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/MockRequest.groovy b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/MockRequest.groovy
similarity index 84%
rename from mockserver/src/main/groovy/pl/touk/mockserver/server/MockRequest.groovy
rename to mockserver/src/main/groovy/eu/ztsh/mockserver/server/MockRequest.groovy
index f8dc561..a714afe 100644
--- a/mockserver/src/main/groovy/pl/touk/mockserver/server/MockRequest.groovy
+++ b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/MockRequest.groovy
@@ -1,9 +1,10 @@
-package pl.touk.mockserver.server
+package eu.ztsh.mockserver.server
import com.sun.net.httpserver.Headers
import groovy.json.JsonSlurper
import groovy.transform.PackageScope
-import groovy.util.slurpersupport.GPathResult
+import groovy.xml.XmlSlurper
+import groovy.xml.slurpersupport.GPathResult
import groovy.xml.XmlUtil
@PackageScope
@@ -27,6 +28,9 @@ class MockRequest {
}
private static GPathResult inputToXml(String text) {
+ if (!text.startsWith('<')) {
+ return null
+ }
try {
return new XmlSlurper().parseText(text)
} catch (Exception _) {
@@ -36,7 +40,7 @@ class MockRequest {
private static GPathResult inputToSoap(GPathResult xml) {
try {
- if (xml.name() == 'Envelope' && xml.Body.size() > 0) {
+ if (xml != null && xml.name() == 'Envelope' && xml.Body.size() > 0) {
return getSoapBodyContent(xml)
} else {
return null
@@ -51,6 +55,9 @@ class MockRequest {
}
private static Object inputToJson(String text) {
+ if (!text.startsWith('[') && !text.startsWith('{')) {
+ return null
+ }
try {
return new JsonSlurper().parseText(text)
} catch (Exception _) {
diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/MockResponse.groovy b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/MockResponse.groovy
similarity index 90%
rename from mockserver/src/main/groovy/pl/touk/mockserver/server/MockResponse.groovy
rename to mockserver/src/main/groovy/eu/ztsh/mockserver/server/MockResponse.groovy
index 913f9f3..260acce 100644
--- a/mockserver/src/main/groovy/pl/touk/mockserver/server/MockResponse.groovy
+++ b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/MockResponse.groovy
@@ -1,4 +1,4 @@
-package pl.touk.mockserver.server
+package eu.ztsh.mockserver.server
import groovy.transform.PackageScope
diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/Util.groovy b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/Util.groovy
similarity index 89%
rename from mockserver/src/main/groovy/pl/touk/mockserver/server/Util.groovy
rename to mockserver/src/main/groovy/eu/ztsh/mockserver/server/Util.groovy
index 3864afa..c51d1f0 100644
--- a/mockserver/src/main/groovy/pl/touk/mockserver/server/Util.groovy
+++ b/mockserver/src/main/groovy/eu/ztsh/mockserver/server/Util.groovy
@@ -1,9 +1,9 @@
-package pl.touk.mockserver.server
+package eu.ztsh.mockserver.server
import com.sun.net.httpserver.HttpExchange
-import pl.touk.mockserver.api.response.MockAdded
+import eu.ztsh.mockserver.api.response.MockAdded
-import javax.xml.bind.JAXBContext
+import jakarta.xml.bind.JAXBContext
class Util {
diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/ContextExecutor.groovy b/mockserver/src/main/groovy/pl/touk/mockserver/server/ContextExecutor.groovy
deleted file mode 100644
index f450df4..0000000
--- a/mockserver/src/main/groovy/pl/touk/mockserver/server/ContextExecutor.groovy
+++ /dev/null
@@ -1,84 +0,0 @@
-package pl.touk.mockserver.server
-
-import com.sun.net.httpserver.HttpExchange
-import groovy.transform.PackageScope
-import groovy.util.logging.Slf4j
-import pl.touk.mockserver.api.common.Method
-
-import java.util.concurrent.CopyOnWriteArrayList
-
-@Slf4j
-@PackageScope
-class ContextExecutor {
- private final HttpServerWraper httpServerWraper
- final String path
- private final List mocks
-
- ContextExecutor(HttpServerWraper httpServerWraper, Mock initialMock) {
- this.httpServerWraper = httpServerWraper
- this.path = "/${initialMock.path}"
- this.mocks = new CopyOnWriteArrayList<>([initialMock])
- httpServerWraper.createContext(path) {
- HttpExchange ex ->
- MockRequest request = new MockRequest(ex.requestBody.text, ex.requestHeaders, ex.requestURI)
- log.info('Mock received input')
- log.debug("Request: ${request.text}")
- for (Mock mock : mocks) {
- try {
- if (mock.match(Method.valueOf(ex.requestMethod), request)) {
- log.debug("Mock ${mock.name} match request ${request.text}")
- MockResponse httpResponse = mock.apply(request)
- fillExchange(ex, httpResponse)
- log.trace("Mock ${mock.name} response with body ${httpResponse.text}")
- return
- }
- log.debug("Mock ${mock.name} does not match request")
- } catch (Exception e) {
- log.warn("An exception occured when matching or applying mock ${mock.name}", e)
- }
- }
- log.warn("Any mock does not match request ${request.text}")
- Util.createResponse(ex, request.text, 404)
- }
- }
-
- String getPath() {
- return path.substring(1)
- }
-
- String getContextPath() {
- return path
- }
-
- private static void fillExchange(HttpExchange httpExchange, MockResponse response) {
- response.headers.each {
- httpExchange.responseHeaders.add(it.key, it.value)
- }
- Util.createResponse(httpExchange, response.text, response.statusCode)
- }
-
- List removeMock(String name) {
- Mock mock = mocks.find { it.name == name }
- if (mock) {
- mocks.remove(mock)
- return mock.history
- }
- return []
- }
-
- List peekMock(String name) {
- Mock mock = mocks.find { it.name == name }
- if (mock) {
- return mock.history
- }
- return []
- }
-
- void addMock(Mock mock) {
- mocks << mock
- }
-
- List getMocks() {
- return mocks
- }
-}
diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWraper.groovy b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWraper.groovy
deleted file mode 100644
index b13b7a2..0000000
--- a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpServerWraper.groovy
+++ /dev/null
@@ -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.Executors
-
-@Slf4j
-@PackageScope
-class HttpServerWraper {
- private final HttpServer httpServer
- final int port
-
- private List executors = []
-
- HttpServerWraper(int port) {
- this.port = port
- InetSocketAddress addr = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), port)
- httpServer = HttpServer.create(addr, 0)
- httpServer.executor = Executors.newWorkStealingPool()
- 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 removeMock(String name) {
- return executors.collect { it.removeMock(name) }.flatten() as List
- }
-
- List peekMock(String name) {
- return executors.collect { it.peekMock(name) }.flatten() as List
- }
-
- List getMocks() {
- return executors.collect { it.mocks }.flatten() as List
- }
-}
diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/Main.groovy b/mockserver/src/main/groovy/pl/touk/mockserver/server/Main.groovy
deleted file mode 100644
index 759a11a..0000000
--- a/mockserver/src/main/groovy/pl/touk/mockserver/server/Main.groovy
+++ /dev/null
@@ -1,20 +0,0 @@
-package pl.touk.mockserver.server
-
-import groovy.util.logging.Slf4j
-
-@Slf4j
-class Main {
- static void main(String[] args) {
- HttpMockServer httpMockServer = args.length == 1 ? new HttpMockServer(args[0] as int) : new HttpMockServer()
-
- Runtime.runtime.addShutdownHook(new Thread({
- log.info('Http server is stopping...')
- httpMockServer.stop()
- log.info('Http server is stopped')
- } as Runnable))
-
- while (true) {
- Thread.sleep(10000)
- }
- }
-}
diff --git a/mvnw b/mvnw
new file mode 100755
index 0000000..8d937f4
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,308 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.2.0
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /usr/local/etc/mavenrc ] ; then
+ . /usr/local/etc/mavenrc
+ fi
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "$(uname)" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
+ else
+ JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=$(java-config --jre-home)
+ fi
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
+ JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="$(which javac)"
+ if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=$(which readlink)
+ if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
+ if $darwin ; then
+ javaHome="$(dirname "\"$javaExecutable\"")"
+ javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
+ else
+ javaExecutable="$(readlink -f "\"$javaExecutable\"")"
+ fi
+ javaHome="$(dirname "\"$javaExecutable\"")"
+ javaHome=$(expr "$javaHome" : '\(.*\)/bin')
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=$(cd "$wdir/.." || exit 1; pwd)
+ fi
+ # end of workaround
+ done
+ printf '%s' "$(cd "$basedir" || exit 1; pwd)"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ # Remove \r in case we run on Windows within Git Bash
+ # and check out the repository with auto CRLF management
+ # enabled. Otherwise, we may read lines that are delimited with
+ # \r\n and produce $'-Xarg\r' rather than -Xarg due to word
+ # splitting rules.
+ tr -s '\r\n' ' ' < "$1"
+ fi
+}
+
+log() {
+ if [ "$MVNW_VERBOSE" = true ]; then
+ printf '%s\n' "$1"
+ fi
+}
+
+BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
+log "$MAVEN_PROJECTBASEDIR"
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
+if [ -r "$wrapperJarPath" ]; then
+ log "Found $wrapperJarPath"
+else
+ log "Couldn't find $wrapperJarPath, downloading it ..."
+
+ if [ -n "$MVNW_REPOURL" ]; then
+ wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ else
+ wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ fi
+ while IFS="=" read -r key value; do
+ # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
+ safeValue=$(echo "$value" | tr -d '\r')
+ case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
+ esac
+ done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+ log "Downloading from: $wrapperUrl"
+
+ if $cygwin; then
+ wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
+ fi
+
+ if command -v wget > /dev/null; then
+ log "Found wget ... using wget"
+ [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ else
+ wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ log "Found curl ... using curl"
+ [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+ else
+ curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+ fi
+ else
+ log "Falling back to using Java to download"
+ javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaSource=$(cygpath --path --windows "$javaSource")
+ javaClass=$(cygpath --path --windows "$javaClass")
+ fi
+ if [ -e "$javaSource" ]; then
+ if [ ! -e "$javaClass" ]; then
+ log " - Compiling MavenWrapperDownloader.java ..."
+ ("$JAVA_HOME/bin/javac" "$javaSource")
+ fi
+ if [ -e "$javaClass" ]; then
+ log " - Running MavenWrapperDownloader.java ..."
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+# If specified, validate the SHA-256 sum of the Maven wrapper jar file
+wrapperSha256Sum=""
+while IFS="=" read -r key value; do
+ case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
+ esac
+done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+if [ -n "$wrapperSha256Sum" ]; then
+ wrapperSha256Result=false
+ if command -v sha256sum > /dev/null; then
+ if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
+ wrapperSha256Result=true
+ fi
+ elif command -v shasum > /dev/null; then
+ if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
+ wrapperSha256Result=true
+ fi
+ else
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
+ echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
+ exit 1
+ fi
+ if [ $wrapperSha256Result = false ]; then
+ echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
+ echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
+ echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
+ exit 1
+ fi
+fi
+
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+# shellcheck disable=SC2086 # safe args
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ $MAVEN_DEBUG_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000..f80fbad
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,205 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.2.0
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %WRAPPER_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
+SET WRAPPER_SHA_256_SUM=""
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
+)
+IF NOT %WRAPPER_SHA_256_SUM%=="" (
+ powershell -Command "&{"^
+ "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
+ "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
+ " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
+ " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
+ " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
+ " exit 1;"^
+ "}"^
+ "}"
+ if ERRORLEVEL 1 goto error
+)
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+ %JVM_CONFIG_MAVEN_PROPS% ^
+ %MAVEN_OPTS% ^
+ %MAVEN_DEBUG_OPTS% ^
+ -classpath %WRAPPER_JAR% ^
+ "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+ %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff --git a/performance-tests/pom.xml b/performance-tests/pom.xml
new file mode 100644
index 0000000..e73db5c
--- /dev/null
+++ b/performance-tests/pom.xml
@@ -0,0 +1,70 @@
+
+
+ 4.0.0
+
+
+ eu.ztsh.mockserver
+ http-mock-server
+ 3.0.0-SNAPSHOT
+
+
+ mockserver-performance-tests
+
+
+
+ eu.ztsh.mockserver
+ mockserver
+
+
+ eu.ztsh.mockserver
+ mockserver-client
+
+
+
+ org.openjdk.jmh
+ jmh-core
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+
+
+
+ 1.4.0
+
+
+
+
+ performance-test
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ ${exec-maven-plugin.version}
+
+
+ run-benchmarks
+ integration-test
+
+ exec
+
+
+ test
+ java
+
+ -classpath
+
+ org.openjdk.jmh.Main
+ .*
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/performance-tests/src/test/java/eu/ztsh/mockserver/client/MockserverTest.java b/performance-tests/src/test/java/eu/ztsh/mockserver/client/MockserverTest.java
new file mode 100644
index 0000000..2a3b943
--- /dev/null
+++ b/performance-tests/src/test/java/eu/ztsh/mockserver/client/MockserverTest.java
@@ -0,0 +1,82 @@
+package eu.ztsh.mockserver.client;
+
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.HttpClients;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.infra.Blackhole;
+import org.openjdk.jmh.infra.ThreadParams;
+import eu.ztsh.mockserver.api.request.AddMock;
+import eu.ztsh.mockserver.server.HttpMockServer;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+@State(Scope.Benchmark)
+@OutputTimeUnit(TimeUnit.SECONDS)
+public class MockserverTest {
+ HttpMockServer httpMockServer;
+
+ int initialPort = 9000;
+
+ @Setup
+ public void prepareMockServer() {
+ httpMockServer = new HttpMockServer(9999);
+ }
+
+ @TearDown
+ public void stopMockServer() {
+ httpMockServer.stop();
+ }
+
+ @State(Scope.Thread)
+ public static class TestState {
+ RemoteMockServer remoteMockServer;
+ HttpClient httpClient;
+ int current;
+
+ @Setup
+ public void prepareMockServer(ThreadParams params) {
+ remoteMockServer = new RemoteMockServer("localhost", 9999);
+ httpClient = HttpClients.createDefault();
+ current = params.getThreadIndex();
+ }
+ }
+
+ @Benchmark
+ @Measurement(iterations = 20)
+ @BenchmarkMode({Mode.AverageTime, Mode.Throughput, Mode.SampleTime})
+ @Warmup(iterations = 10)
+ public void shouldHandleManyRequestsSimultaneously(TestState testState, Blackhole bh) throws IOException {
+ int current = testState.current;
+ int endpointNumber = current % 10;
+ int port = initialPort + (current % 7);
+ AddMock addMock = new AddMock();
+ addMock.setName("testRest" + current);
+ addMock.setPath("testEndpoint" + endpointNumber);
+ addMock.setPort(port);
+ addMock.setPredicate("{req -> req.xml.name() == 'request" + current + "' }");
+ addMock.setResponse("{req -> ''}");
+ testState.remoteMockServer.addMock(addMock);
+ HttpPost restPost = new HttpPost("http://localhost:" + port + "/testEndpoint" + endpointNumber);
+ restPost.setEntity(new StringEntity("", ContentType.create("text/xml", "UTF-8")));
+ CloseableHttpResponse response = (CloseableHttpResponse) testState.httpClient.execute(restPost);
+ String stringResponse = Util.extractStringResponse(response);
+ testState.remoteMockServer.removeMock("testRest" + current, true);
+ assert stringResponse.equals("");
+ bh.consume(stringResponse);
+ }
+
+}
diff --git a/performance-tests/src/test/resources/logback.xml b/performance-tests/src/test/resources/logback.xml
new file mode 100644
index 0000000..b37e533
--- /dev/null
+++ b/performance-tests/src/test/resources/logback.xml
@@ -0,0 +1,15 @@
+
+
+
+ %highlight(%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n)
+
+
+
+
+ %msg%n
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index c486df6..a981985 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,63 +1,90 @@
-
+
4.0.0
-
- pl.touk
- top
- 1.0.7
-
-
- pl.touk.mockserver
+ eu.ztsh.mockserver
http-mock-server
pom
- 2.2.0
+ 3.0.0-SNAPSHOT
+
mockserver-client
mockserver
mockserver-tests
mockserver-api
+ performance-tests
+ 11
+ ${java.version}
+ ${java.version}
UTF-8
+
UTF-8
- 3.1
- 2.4.1
- 4.3.5
- 1.0-groovy-2.4
+ 4.0.12
+ 4.5.13
+ 2.2-groovy-4.0
3.3.2
- 1.7.7
- 1.0.13
- 1.16.6
+ 1.7.30
+ 1.3.12
+ 1.18.26
+ 4.0.4
true
+ 1.37
+ 3.0.2
+ 3.1.0
-
- scm:git:git@github.com:TouK/http-mock-server.git
- scm:git:git@github.com:TouK/http-mock-server.git
- http-mock-server-2.2.0
-
-
- org.codehaus.groovy
- groovy-all
+ eu.ztsh.mockserver
+ mockserver-api
+ ${project.version}
+
+
+ eu.ztsh.mockserver
+ mockserver
+ ${project.version}
+
+
+ eu.ztsh.mockserver
+ mockserver-client
+ ${project.version}
+
+
+
+ org.glassfish.jaxb
+ jaxb-bom
+ ${jaxb.version}
+ pom
+ import
+
+
+
+ org.apache.groovy
+ groovy
${groovy.version}
+
+ org.apache.groovy
+ groovy-json
+ ${groovy.version}
+
+
+ org.apache.groovy
+ groovy-xml
+ ${groovy.version}
+
+
org.apache.httpcomponents
httpclient
${httpclient.version}
-
- org.spockframework
- spock-core
- ${spock-core.version}
- test
-
org.apache.commons
commons-lang3
@@ -68,64 +95,66 @@
slf4j-api
${slf4j-api.version}
+
+ ch.qos.logback
+ logback-core
+ ${logback.version}
+
ch.qos.logback
logback-classic
- ${logback-classic.version}
+ ${logback.version}
org.projectlombok
lombok
${lombok.version}
+
- pl.touk.mockserver
- mockserver-api
- ${project.version}
+ org.spockframework
+ spock-core
+ ${spock-core.version}
+ test
+
+
+ org.openjdk.jmh
+ jmh-core
+ ${jmh.version}
+ test
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ ${jmh.version}
+ test
- clean install
-
-
- org.codehaus.gmavenplus
- gmavenplus-plugin
- 1.4
-
-
-
- compile
- testCompile
-
-
-
-
-
- org.apache.maven.plugins
- maven-release-plugin
- 2.5.2
-
- true
-
-
-
+
+
+
+ org.codehaus.mojo
+ jaxb2-maven-plugin
+ ${jaxb2-maven-plugin.version}
+
+
+ org.codehaus.gmavenplus
+ gmavenplus-plugin
+ ${gmavenplus-plugin.version}
+
+
+
+ compile
+ compileTests
+
+
+
+
+
+
-
-
- Dominik Przybysz
- alien11689@gmail.com
-
-
-
-
-
- touk
- TouK Open source repository
- https://philanthropist.touk.pl/nexus/content/repositories/releases
-
-
-