├── moco-core
└── src
│ ├── test
│ ├── resources
│ │ ├── test
│ │ │ ├── foo.bar
│ │ │ └── dir.response
│ │ ├── foo.request
│ │ ├── foo.response
│ │ ├── foo.template
│ │ ├── bar.xml
│ │ ├── foo.xml
│ │ ├── foobar.xml
│ │ ├── failover.response
│ │ └── many_content_failover.response
│ └── java
│ │ └── com
│ │ └── github
│ │ └── dreamhead
│ │ └── moco
│ │ ├── RemoteTestUtils.java
│ │ ├── AbstractMocoTest.java
│ │ ├── internal
│ │ └── MocoHttpServerTest.java
│ │ ├── util
│ │ └── FileContentTypeTest.java
│ │ ├── MocoDefenseTest.java
│ │ ├── mount
│ │ └── MountToTest.java
│ │ ├── extractor
│ │ ├── UriRequestExtractorTest.java
│ │ ├── VersionExtractorTest.java
│ │ ├── HttpMethodExtractorTest.java
│ │ └── ParamRequestExtractorTest.java
│ │ ├── helper
│ │ └── MocoTestHelper.java
│ │ ├── MocoWebTest.java
│ │ ├── MocoJsonTest.java
│ │ ├── handler
│ │ └── failover
│ │ │ └── DefaultRequestTest.java
│ │ ├── MocoFileRootTest.java
│ │ ├── MocoXmlTest.java
│ │ ├── MocoMountTest.java
│ │ └── MocoContextTest.java
│ └── main
│ ├── java
│ └── com
│ │ └── github
│ │ └── dreamhead
│ │ └── moco
│ │ ├── Runnable.java
│ │ ├── dumper
│ │ ├── Dumper.java
│ │ ├── HttpMessageBaseDumper.java
│ │ ├── HttpResponseDumper.java
│ │ └── HttpRequestDumper.java
│ │ ├── resource
│ │ ├── Identifiable.java
│ │ ├── Content.java
│ │ ├── ResourceReader.java
│ │ ├── ResourceConfigApplier.java
│ │ ├── reader
│ │ │ ├── ContentResourceReader.java
│ │ │ ├── FileResourceReader.java
│ │ │ ├── ClasspathFileResourceReader.java
│ │ │ └── TemplateResourceReader.java
│ │ ├── IdFactory.java
│ │ ├── ContentResource.java
│ │ └── Resource.java
│ │ ├── ConfigApplier.java
│ │ ├── MocoConfig.java
│ │ ├── mount
│ │ ├── MountPredicate.java
│ │ ├── MountPathExtractor.java
│ │ ├── MountTo.java
│ │ ├── MountMatcher.java
│ │ └── MountHandler.java
│ │ ├── RequestExtractor.java
│ │ ├── RequestMatcher.java
│ │ ├── Setting.java
│ │ ├── model
│ │ ├── Request.java
│ │ ├── Response.java
│ │ ├── Session.java
│ │ ├── DefaultRequest.java
│ │ ├── MessageFactory.java
│ │ └── Message.java
│ │ ├── ResponseHandler.java
│ │ ├── extractor
│ │ ├── VersionExtractor.java
│ │ ├── HttpMethodExtractor.java
│ │ ├── ContentRequestExtractor.java
│ │ ├── UriRequestExtractor.java
│ │ ├── XmlExtractorHelper.java
│ │ ├── HeaderRequestExtractor.java
│ │ ├── CookieRequestExtractor.java
│ │ ├── ParamRequestExtractor.java
│ │ ├── JsonPathRequestExtractor.java
│ │ ├── Extractors.java
│ │ ├── FormRequestExtractor.java
│ │ └── XPathRequestExtractor.java
│ │ ├── handler
│ │ ├── ContentTypeDetector.java
│ │ ├── StatusCodeResponseHandler.java
│ │ ├── failover
│ │ │ └── Failover.java
│ │ ├── LatencyResponseHandler.java
│ │ ├── VersionResponseHandler.java
│ │ ├── ContentHandler.java
│ │ ├── AbstractContentResponseHandler.java
│ │ ├── HeaderResponseHandler.java
│ │ ├── AndResponseHandler.java
│ │ ├── ResponseHandlers.java
│ │ └── SequenceContentHandler.java
│ │ ├── Runner.java
│ │ ├── config
│ │ ├── MocoContextConfig.java
│ │ └── MocoFileRootConfig.java
│ │ ├── util
│ │ ├── Configs.java
│ │ ├── Cookies.java
│ │ └── FileContentType.java
│ │ ├── matcher
│ │ ├── MatchMatcher.java
│ │ ├── AndRequestMatcher.java
│ │ ├── OrRequestMatcher.java
│ │ ├── EqRequestMatcher.java
│ │ ├── CompositeRequestMatcher.java
│ │ ├── AbstractOperatorMatcher.java
│ │ └── JsonRequestMatcher.java
│ │ ├── MocoMount.java
│ │ ├── internal
│ │ ├── MocoClient.java
│ │ ├── MocoEventListener.java
│ │ ├── MocoHttpServer.java
│ │ ├── MocoServer.java
│ │ └── MocoHandler.java
│ │ ├── ResponseSetting.java
│ │ ├── HttpServer.java
│ │ └── setting
│ │ └── BaseSetting.java
│ └── resources
│ └── logback.xml
├── moco-runner
└── src
│ ├── test
│ ├── resources
│ │ ├── foo.request
│ │ ├── mount
│ │ │ ├── foo.bar
│ │ │ └── mount.response
│ │ ├── foo.response
│ │ ├── template.foo
│ │ ├── path.request
│ │ ├── path.response
│ │ ├── xml.xml
│ │ ├── any_response_with_file.json
│ │ ├── multiple
│ │ │ ├── fileroot-settings.json
│ │ │ ├── settings.json
│ │ │ ├── bar.json
│ │ │ ├── foo.json
│ │ │ ├── fileroot.json
│ │ │ ├── context-settings.json
│ │ │ └── env-settings.json
│ │ ├── get_method.json
│ │ ├── put_method.json
│ │ ├── post_method.json
│ │ ├── delete_method.json
│ │ ├── form.json
│ │ ├── query.json
│ │ ├── json.json
│ │ ├── jsonpath.json
│ │ ├── header.json
│ │ ├── xpath.json
│ │ ├── redirect.json
│ │ ├── path_resource.json
│ │ ├── standalone_failover.json
│ │ ├── xml.json
│ │ ├── mount.json
│ │ ├── proxy.json
│ │ ├── match.json
│ │ └── template.json
│ └── java
│ │ └── com
│ │ └── github
│ │ └── dreamhead
│ │ └── moco
│ │ ├── MocoRedirectStandaloneTest.java
│ │ ├── MocoMulipleConfigurationsTest.java
│ │ ├── MocoProxyStandaloneTest.java
│ │ ├── MocoJsonStandaloneTest.java
│ │ ├── bootstrap
│ │ ├── ShutdownArgsTest.java
│ │ └── StartArgsTest.java
│ │ ├── MocoPathResourceTest.java
│ │ ├── AbstractMocoStandaloneTest.java
│ │ ├── runner
│ │ ├── DynamicConfigurationRunnerTest.java
│ │ ├── AbstractRunnerTest.java
│ │ ├── DynamicSettingRunnerTest.java
│ │ └── ShutdownMonitorRunnerTest.java
│ │ ├── MocoXmlStandaloneTest.java
│ │ ├── MocoMountStandaloneTest.java
│ │ ├── MocoMatchTest.java
│ │ ├── MocoTemplateStandaloneTest.java
│ │ └── parser
│ │ └── GlobalSettingParserTest.java
│ └── main
│ ├── resources
│ └── favicon.png
│ └── java
│ └── com
│ └── github
│ └── dreamhead
│ └── moco
│ ├── runner
│ ├── Runner.java
│ ├── monitor
│ │ ├── ShutdownListener.java
│ │ ├── Monitor.java
│ │ ├── FilesMonitor.java
│ │ ├── MonitorFactory.java
│ │ ├── FileMonitor.java
│ │ └── ShutdownMonitor.java
│ ├── MonitorRunner.java
│ ├── RunnerSetting.java
│ ├── StandaloneRunner.java
│ ├── FileRunner.java
│ ├── RunnerFactory.java
│ └── SettingRunner.java
│ ├── bootstrap
│ ├── BootstrapTask.java
│ ├── Main.java
│ ├── ParseArgException.java
│ ├── ShutdownArgs.java
│ ├── ShutdownPortOption.java
│ ├── StartTask.java
│ ├── Bootstrap.java
│ └── ShutdownTask.java
│ └── parser
│ ├── RequestMatcherFactory.java
│ ├── model
│ ├── GlobalSetting.java
│ ├── ProxyContainer.java
│ ├── TextContainer.java
│ ├── TextContainerDeserializer.java
│ ├── RequestSetting.java
│ ├── MountSetting.java
│ ├── ProxyContainerDeserializer.java
│ ├── SessionSetting.java
│ └── Dynamics.java
│ ├── GlobalSettingParser.java
│ └── HttpServerParser.java
├── moco-doc
├── configurations.md
├── moco.png
├── DukeChoice-960x90-lm.png
├── plan.md
└── ReleaseNote.md
├── settings.gradle
├── .gitignore
├── .travis.yml
├── gradle.properties
├── MIT-LICENSE.txt
└── README.md
/moco-core/src/test/resources/test/foo.bar:
--------------------------------------------------------------------------------
1 | foo.bar
--------------------------------------------------------------------------------
/moco-core/src/test/resources/foo.request:
--------------------------------------------------------------------------------
1 | foo.request
--------------------------------------------------------------------------------
/moco-core/src/test/resources/foo.response:
--------------------------------------------------------------------------------
1 | foo.response
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/foo.request:
--------------------------------------------------------------------------------
1 | foo.request
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/mount/foo.bar:
--------------------------------------------------------------------------------
1 | foo.bar
--------------------------------------------------------------------------------
/moco-core/src/test/resources/foo.template:
--------------------------------------------------------------------------------
1 | ${req.method}
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/foo.response:
--------------------------------------------------------------------------------
1 | foo.response
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/template.foo:
--------------------------------------------------------------------------------
1 | ${req.method}
--------------------------------------------------------------------------------
/moco-core/src/test/resources/test/dir.response:
--------------------------------------------------------------------------------
1 | response from dir
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/path.request:
--------------------------------------------------------------------------------
1 | request from path
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/path.response:
--------------------------------------------------------------------------------
1 | response from path
--------------------------------------------------------------------------------
/moco-doc/configurations.md:
--------------------------------------------------------------------------------
1 | moved to [APIs](/moco-doc/apis.md)
2 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/mount/mount.response:
--------------------------------------------------------------------------------
1 | response from mount
--------------------------------------------------------------------------------
/moco-doc/moco.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/moco/master/moco-doc/moco.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'moco'
2 | include 'moco-core', 'moco-runner'
3 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/xml.xml:
--------------------------------------------------------------------------------
1 | 1
--------------------------------------------------------------------------------
/moco-doc/DukeChoice-960x90-lm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/moco/master/moco-doc/DukeChoice-960x90-lm.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | target
3 | build
4 | *.iml
5 | *.ipr
6 | *.iws
7 | out
8 | reports
9 | .DS_Store
10 | .idea
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | jdk:
3 | - oraclejdk7
4 | - openjdk7
5 | - openjdk6
6 |
7 | script:
8 | "gradle check -i"
--------------------------------------------------------------------------------
/moco-core/src/test/resources/bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 2
4 |
5 |
--------------------------------------------------------------------------------
/moco-core/src/test/resources/foo.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
--------------------------------------------------------------------------------
/moco-runner/src/main/resources/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/moco/master/moco-runner/src/main/resources/favicon.png
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Default config properties, can be overridden by ~/.gradle/gradle.properties
2 | sonatypeUsername=
3 | sonatypePassword=
4 |
--------------------------------------------------------------------------------
/moco-core/src/test/resources/foobar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 | 2
5 |
6 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/Runnable.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | public interface Runnable {
4 | void run() throws Exception;
5 | }
6 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/dumper/Dumper.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.dumper;
2 |
3 | public interface Dumper {
4 | String dump(T message);
5 | }
6 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/resource/Identifiable.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.resource;
2 |
3 | public interface Identifiable {
4 | String id();
5 | }
6 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/any_response_with_file.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "response" :
4 | {
5 | "file" : "src/test/resources/foo.response"
6 | }
7 | }
8 | ]
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/resource/Content.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.resource;
2 |
3 | public interface Content {
4 | String getContentType();
5 | }
6 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/runner/Runner.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner;
2 |
3 | public interface Runner {
4 | void run();
5 | void stop();
6 | }
7 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/ConfigApplier.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | public interface ConfigApplier {
4 | T apply(final MocoConfig config);
5 | }
6 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/BootstrapTask.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.bootstrap;
2 |
3 | public interface BootstrapTask {
4 | void run(String[] args);
5 | }
6 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/MocoConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | public interface MocoConfig {
4 | boolean isFor(String id);
5 | String apply(String target);
6 | }
7 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/runner/monitor/ShutdownListener.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner.monitor;
2 |
3 | public interface ShutdownListener {
4 | void onShutdown();
5 | }
6 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/multiple/fileroot-settings.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "context": "/fileroot",
4 | "file_root": "src/test/resources/",
5 | "include": "multiple/fileroot.json"
6 | }
7 | ]
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/multiple/settings.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "include" : "src/test/resources/multiple/foo.json"
4 | },
5 | {
6 | "include" : "src/test/resources/multiple/bar.json"
7 | }
8 | ]
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/runner/monitor/Monitor.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner.monitor;
2 |
3 | public interface Monitor {
4 | void startMonitor();
5 | void stopMonitor();
6 | }
7 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/multiple/bar.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request" : {
4 | "uri" : "/bar"
5 | },
6 | "response" : {
7 | "text" : "bar"
8 | }
9 | }
10 | ]
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/multiple/foo.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request" : {
4 | "uri" : "/foo"
5 | },
6 | "response" : {
7 | "text" : "foo"
8 | }
9 | }
10 | ]
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/mount/MountPredicate.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.mount;
2 |
3 | import com.google.common.base.Predicate;
4 |
5 | public interface MountPredicate extends Predicate {
6 | }
7 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/multiple/fileroot.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request" : {
4 | "uri" : "/fileroot"
5 | },
6 | "response" : {
7 | "file" : "foo.response"
8 | }
9 | }
10 | ]
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/Main.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.bootstrap;
2 |
3 | public class Main {
4 | public static void main(String[] args) {
5 | new Bootstrap().run(args);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/RequestExtractor.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import io.netty.handler.codec.http.FullHttpRequest;
4 |
5 | public interface RequestExtractor {
6 | T extract(FullHttpRequest request);
7 | }
8 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/resource/ResourceReader.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.resource;
2 |
3 | import io.netty.handler.codec.http.FullHttpRequest;
4 |
5 | public interface ResourceReader {
6 | byte[] readFor(FullHttpRequest request);
7 | }
8 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/get_method.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request" :
4 | {
5 | "method" : "get",
6 | "uri" : "/get"
7 | },
8 | "response" :
9 | {
10 | "text" : "response_for_get_method"
11 | }
12 | }
13 | ]
14 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/put_method.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request" :
4 | {
5 | "method" : "put",
6 | "uri" : "/put"
7 | },
8 | "response" :
9 | {
10 | "text" : "response_for_put_method"
11 | }
12 | }
13 | ]
14 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/post_method.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request" :
4 | {
5 | "method" : "post",
6 | "uri" : "/post"
7 | },
8 | "response" :
9 | {
10 | "text" : "response_for_post_method"
11 | }
12 | }
13 | ]
14 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/delete_method.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request" :
4 | {
5 | "method" : "delete",
6 | "uri" : "/delete"
7 | },
8 | "response" :
9 | {
10 | "text" : "response_for_delete_method"
11 | }
12 | }
13 | ]
14 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/multiple/context-settings.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "context": "/foo",
4 | "include": "src/test/resources/multiple/foo.json"
5 | },
6 | {
7 | "context": "/bar",
8 | "include": "src/test/resources/multiple/bar.json"
9 | }
10 | ]
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/RequestMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import io.netty.handler.codec.http.FullHttpRequest;
4 |
5 | public interface RequestMatcher extends ConfigApplier {
6 | boolean match(final FullHttpRequest request);
7 | }
8 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/resource/ResourceConfigApplier.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.resource;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 |
5 | public interface ResourceConfigApplier {
6 | public Resource apply(MocoConfig config, Resource resource);
7 | }
8 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/form.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request" : {
4 | "method" : "post",
5 | "forms" : {
6 | "name" : "dreamhead"
7 | }
8 | },
9 | "response" : {
10 | "text" : "foobar"
11 | }
12 | }
13 | ]
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/Setting.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | public abstract class Setting extends ResponseSetting {
4 | protected final RequestMatcher matcher;
5 |
6 | protected Setting(RequestMatcher matcher) {
7 | this.matcher = matcher;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/query.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request" :
4 | {
5 | "uri" : "/query",
6 | "queries" : {
7 | "param" : "foo"
8 | }
9 | },
10 | "response" :
11 | {
12 | "text" : "response_for_query_request"
13 | }
14 | }
15 | ]
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/json.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request": {
4 | "uri": "/json",
5 | "text": {
6 | "json": "{\"foo\":\"bar\"}"
7 | }
8 | },
9 | "response": {
10 | "text": "response_for_json_request"
11 | }
12 | }
13 | ]
14 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/jsonpath.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request": {
4 | "uri": "/jsonpath",
5 | "json_paths": {
6 | "$.book[*].price": "1"
7 | }
8 | },
9 | "response": {
10 | "text": "response_for_json_path_request"
11 | }
12 | }
13 | ]
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/resource/reader/ContentResourceReader.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.resource.reader;
2 |
3 | import com.github.dreamhead.moco.resource.Content;
4 | import com.github.dreamhead.moco.resource.ResourceReader;
5 |
6 | public interface ContentResourceReader extends ResourceReader, Content {
7 | }
8 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/multiple/env-settings.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "context": "/foo",
4 | "env": "foo",
5 | "include": "src/test/resources/multiple/foo.json"
6 | },
7 | {
8 | "context": "/bar",
9 | "env": "bar",
10 | "include": "src/test/resources/multiple/bar.json"
11 | }
12 | ]
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/header.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request" :
4 | {
5 | "uri" : "/header",
6 | "headers" : {
7 | "content-type" : "application/json"
8 | }
9 | },
10 | "response" :
11 | {
12 | "text" : "response_for_header_request"
13 | }
14 | }
15 | ]
16 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/xpath.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request" :
4 | {
5 | "uri" : "/xpath",
6 | "xpaths" : {
7 | "/request/parameters/id/text()" : "1"
8 | }
9 | },
10 | "response" :
11 | {
12 | "text" : "response_for_xpath_request"
13 | }
14 | }
15 | ]
16 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/redirect.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request" :
4 | {
5 | "uri" : "/redirectTo"
6 | },
7 | "response" :
8 | {
9 | "text" : "foo"
10 | }
11 | },
12 | {
13 | "request" :
14 | {
15 | "uri" : "/redirect"
16 | },
17 |
18 | "redirectTo" : "http://localhost:12306/redirectTo"
19 | }
20 | ]
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/parser/RequestMatcherFactory.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.parser;
2 |
3 | import com.github.dreamhead.moco.RequestMatcher;
4 | import com.github.dreamhead.moco.parser.model.RequestSetting;
5 |
6 | public interface RequestMatcherFactory {
7 | RequestMatcher createRequestMatcher(RequestSetting request);
8 | }
9 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/model/Request.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.model;
2 |
3 | import java.util.Map;
4 |
5 | public interface Request {
6 | Map getQueries();
7 |
8 | String getMethod();
9 |
10 | String getVersion();
11 |
12 | String getContent();
13 |
14 | Map getHeaders();
15 | }
16 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/ResponseHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import io.netty.handler.codec.http.FullHttpRequest;
4 | import io.netty.handler.codec.http.FullHttpResponse;
5 |
6 | public interface ResponseHandler extends ConfigApplier {
7 | void writeToResponse(FullHttpRequest request, FullHttpResponse response);
8 | }
9 |
--------------------------------------------------------------------------------
/moco-core/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | %d{dd MMM yyyy HH:mm:ss} [%thread] %-5level %msg%n
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/ParseArgException.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.bootstrap;
2 |
3 | public class ParseArgException extends RuntimeException {
4 | public ParseArgException(String s) {
5 | super(s);
6 | }
7 |
8 | public ParseArgException(String s, Throwable throwable) {
9 | super(s, throwable);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/resource/IdFactory.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.resource;
2 |
3 | public class IdFactory {
4 | public static Identifiable id(final String id) {
5 | return new Identifiable() {
6 | @Override
7 | public String id() {
8 | return id;
9 | }
10 | };
11 | }
12 |
13 | private IdFactory() {}
14 | }
15 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/path_resource.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request": {
4 | "uri": "/path-resource"
5 | },
6 | "response": {
7 | "path_resource": "path.response"
8 | }
9 | },
10 | {
11 | "request": {
12 | "path_resource": "path.request"
13 | },
14 | "response": {
15 | "text": "path resource"
16 | }
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/extractor/VersionExtractor.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.extractor;
2 |
3 | import com.github.dreamhead.moco.RequestExtractor;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 |
6 | public class VersionExtractor implements RequestExtractor {
7 | @Override
8 | public String extract(FullHttpRequest request) {
9 | return request.getProtocolVersion().toString();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/extractor/HttpMethodExtractor.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.extractor;
2 |
3 | import com.github.dreamhead.moco.RequestExtractor;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 |
6 | public class HttpMethodExtractor implements RequestExtractor {
7 | @Override
8 | public String extract(FullHttpRequest request) {
9 | return request.getMethod().name().toUpperCase();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/standalone_failover.json:
--------------------------------------------------------------------------------
1 | [ {
2 | "request" : {
3 | "version" : "HTTP/1.1",
4 | "headers" : {
5 | "Host" : "localhost:12306"
6 | }
7 | },
8 | "response" : {
9 | "version" : "HTTP/1.1",
10 | "content" : "proxy",
11 | "headers" : {
12 | "Content-Length" : "5",
13 | "Content-Type" : "text/plain; charset=UTF-8"
14 | },
15 | "statusCode" : 200
16 | }
17 | } ]
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/handler/ContentTypeDetector.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.handler;
2 |
3 | import io.netty.handler.codec.http.HttpResponse;
4 |
5 | public class ContentTypeDetector {
6 | public boolean hasContentType(HttpResponse response) {
7 | return hasHeader(response, "Content-Type");
8 | }
9 |
10 | public boolean hasHeader(HttpResponse response, String headerName) {
11 | return response.headers().contains(headerName);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/model/Response.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonAutoDetect;
4 |
5 | @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
6 | public class Response extends Message {
7 | private int statusCode;
8 |
9 | public void setStatusCode(int statusCode) {
10 | this.statusCode = statusCode;
11 | }
12 |
13 | public int getStatusCode() {
14 | return statusCode;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/extractor/ContentRequestExtractor.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.extractor;
2 |
3 | import com.github.dreamhead.moco.RequestExtractor;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 |
6 | import java.nio.charset.Charset;
7 |
8 | public class ContentRequestExtractor implements RequestExtractor {
9 | @Override
10 | public String extract(FullHttpRequest request) {
11 | return request.content().toString(Charset.defaultCharset());
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/RemoteTestUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | public class RemoteTestUtils {
4 | private static final int PORT = 12306;
5 | private static final String BASE_URL = "http://localhost:" + PORT;
6 |
7 | public static int port() {
8 | return PORT;
9 | }
10 |
11 | public static String root() {
12 | return BASE_URL;
13 | }
14 |
15 | public static String remoteUrl(String uri) {
16 | return root() + uri;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/extractor/UriRequestExtractor.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.extractor;
2 |
3 | import com.github.dreamhead.moco.RequestExtractor;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 | import io.netty.handler.codec.http.QueryStringDecoder;
6 |
7 | public class UriRequestExtractor implements RequestExtractor {
8 | @Override
9 | public String extract(FullHttpRequest request) {
10 | return new QueryStringDecoder(request.getUri()).path();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/moco-doc/plan.md:
--------------------------------------------------------------------------------
1 | # Functionality
2 |
3 | ## Configuration
4 |
5 | ### Matcher
6 | * Marshal support
7 |
8 | ### Handler
9 | * Template function
10 |
11 | ## More
12 | * Async
13 | * REST
14 | * HTTPS
15 |
16 | ## Client
17 | * Socket
18 |
19 | # Document
20 | * Cookbook
21 |
22 | # Tools Support
23 | * JUnit better support
24 | * Proguard to reduce uberjar size
25 | * homebrew
26 | * IntelliJ IDEA plugin
27 | * Eclipse plugin
28 | * Runtime configuration change
29 |
30 | # Polyglot
31 | * Groovy
32 | * Scala
33 | * Clojure
34 | * Node.js
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/extractor/XmlExtractorHelper.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.extractor;
2 |
3 | import com.github.dreamhead.moco.RequestExtractor;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 | import org.xml.sax.InputSource;
6 |
7 | import java.io.StringReader;
8 |
9 | public class XmlExtractorHelper {
10 | public InputSource extractAsInputSource(FullHttpRequest request, RequestExtractor extractor) {
11 | return new InputSource(new StringReader(extractor.extract(request)));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/resource/ContentResource.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.resource;
2 |
3 | import com.github.dreamhead.moco.resource.reader.ContentResourceReader;
4 |
5 | public class ContentResource extends Resource implements Content {
6 | public ContentResource(Identifiable identifiable, ResourceConfigApplier configApplier, ContentResourceReader reader) {
7 | super(identifiable, configApplier, reader);
8 | }
9 |
10 | public String getContentType() {
11 | return ((ContentResourceReader)reader).getContentType();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/Runner.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import com.github.dreamhead.moco.internal.ActualHttpServer;
4 | import com.github.dreamhead.moco.internal.MocoHttpServer;
5 |
6 | public class Runner {
7 | public static void running(HttpServer httpServer, Runnable runnable) throws Exception {
8 | MocoHttpServer server = new MocoHttpServer((ActualHttpServer)httpServer);
9 | try {
10 | server.start();
11 | runnable.run();
12 | } finally {
13 | server.stop();
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/extractor/HeaderRequestExtractor.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.extractor;
2 |
3 | import com.github.dreamhead.moco.RequestExtractor;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 |
6 | public class HeaderRequestExtractor implements RequestExtractor {
7 | private final String name;
8 |
9 | public HeaderRequestExtractor(String name) {
10 | this.name = name;
11 | }
12 |
13 | @Override
14 | public String extract(FullHttpRequest request) {
15 | return request.headers().get(name);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/config/MocoContextConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.config;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 |
5 | public class MocoContextConfig implements MocoConfig {
6 | private final String context;
7 |
8 | public MocoContextConfig(String context) {
9 | this.context = context;
10 | }
11 |
12 | @Override
13 | public boolean isFor(String id) {
14 | return "uri".equalsIgnoreCase(id);
15 | }
16 |
17 | @Override
18 | public String apply(String uri) {
19 | return context + uri;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/AbstractMocoTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import com.github.dreamhead.moco.helper.MocoTestHelper;
4 | import org.junit.Before;
5 |
6 | import static com.github.dreamhead.moco.Moco.httpserver;
7 | import static com.github.dreamhead.moco.RemoteTestUtils.port;
8 |
9 | public class AbstractMocoTest {
10 | protected HttpServer server;
11 | protected MocoTestHelper helper;
12 |
13 | @Before
14 | public void setUp() throws Exception {
15 | helper = new MocoTestHelper();
16 | server = httpserver(port());
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/moco-core/src/test/resources/failover.response:
--------------------------------------------------------------------------------
1 | [ {
2 | "request" : {
3 | "version" : "HTTP/1.1",
4 | "content" : "proxy",
5 | "headers" : {
6 | "Host" : "localhost:12306",
7 | "Content-Length" : "5",
8 | "User-Agent" : "Apache-HttpClient/4.2.5 (java 1.5)",
9 | "Connection" : "Keep-Alive"
10 | },
11 | "method" : "POST"
12 | },
13 | "response" : {
14 | "version" : "HTTP/1.1",
15 | "content" : "proxy",
16 | "headers" : {
17 | "Content-Length" : "5",
18 | "Content-Type" : "text/plain; charset=UTF-8"
19 | },
20 | "statusCode" : 200
21 | }
22 | } ]
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/util/Configs.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.util;
2 |
3 | import com.github.dreamhead.moco.ConfigApplier;
4 | import com.github.dreamhead.moco.MocoConfig;
5 |
6 | public class Configs {
7 | public static > T configItem(T source, MocoConfig... configs) {
8 | if (source == null) {
9 | return null;
10 | }
11 |
12 | T target = source;
13 | for (MocoConfig config : configs) {
14 | target = target.apply(config);
15 | }
16 | return target;
17 | }
18 |
19 | private Configs() {}
20 | }
21 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/mount/MountPathExtractor.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.mount;
2 |
3 | import com.github.dreamhead.moco.extractor.UriRequestExtractor;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 |
6 | public class MountPathExtractor {
7 | private final MountTo target;
8 | private final UriRequestExtractor extractor = new UriRequestExtractor();
9 |
10 | public MountPathExtractor(MountTo target) {
11 | this.target = target;
12 | }
13 |
14 | public String extract(FullHttpRequest request) {
15 | return target.extract(extractor.extract(request));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/xml.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request": {
4 | "uri": "/xml",
5 | "text": {
6 | "xml": "1"
7 | }
8 | },
9 | "response": {
10 | "text": "response_for_xml_request"
11 | }
12 | },
13 | {
14 | "request": {
15 | "uri": "/xmlfile",
16 | "file": {
17 | "xml": "src/test/resources/xml.xml"
18 | }
19 | },
20 | "response": {
21 | "text": "response_for_xml_file_request"
22 | }
23 | }
24 | ]
25 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/internal/MocoHttpServerTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.internal;
2 |
3 | import org.junit.Test;
4 |
5 | public class MocoHttpServerTest {
6 | @Test
7 | public void should_stop_stoped_server_without_exception() {
8 | MocoHttpServer server = new MocoHttpServer(new ActualHttpServer(8080));
9 | server.stop();
10 | }
11 |
12 | @Test
13 | public void should_stop_server_many_times_without_exception() {
14 | MocoHttpServer server = new MocoHttpServer(new ActualHttpServer(8080));
15 | server.start();
16 | server.stop();
17 | server.stop();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/MocoRedirectStandaloneTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import org.junit.Test;
4 |
5 | import java.io.IOException;
6 |
7 | import static com.github.dreamhead.moco.RemoteTestUtils.remoteUrl;
8 | import static org.hamcrest.CoreMatchers.is;
9 | import static org.junit.Assert.assertThat;
10 |
11 | public class MocoRedirectStandaloneTest extends AbstractMocoStandaloneTest {
12 | @Test
13 | public void should_redirect_to_expected_url() throws IOException {
14 | runWithConfiguration("redirect.json");
15 |
16 | assertThat(helper.get(remoteUrl("/redirect")), is("foo"));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/config/MocoFileRootConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.config;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 |
5 | import java.io.File;
6 |
7 | public class MocoFileRootConfig implements MocoConfig {
8 | private final String fileRoot;
9 |
10 | public MocoFileRootConfig(String fileRoot) {
11 | this.fileRoot = fileRoot;
12 | }
13 |
14 | @Override
15 | public boolean isFor(String id) {
16 | return "file".equals(id);
17 | }
18 |
19 | @Override
20 | public String apply(String filename) {
21 | return this.fileRoot + File.separator + filename;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/runner/MonitorRunner.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner;
2 |
3 | import com.github.dreamhead.moco.runner.monitor.Monitor;
4 |
5 | public class MonitorRunner implements Runner {
6 | private final Runner runner;
7 | private final Monitor monitor;
8 |
9 | public MonitorRunner(Runner runner, Monitor monitor) {
10 | this.runner = runner;
11 | this.monitor = monitor;
12 | }
13 |
14 | @Override
15 | public void run() {
16 | this.runner.run();
17 | this.monitor.startMonitor();
18 | }
19 |
20 | @Override
21 | public void stop() {
22 | this.monitor.stopMonitor();
23 | this.runner.stop();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/mount.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "mount" :
4 | {
5 | "dir" : "src/test/resources/mount",
6 | "uri" : "/mount"
7 | }
8 | },
9 | {
10 | "mount" :
11 | {
12 | "dir" : "src/test/resources/mount",
13 | "uri" : "/mount-include",
14 | "includes" :
15 | [
16 | "*.response"
17 | ]
18 | }
19 | },
20 | {
21 | "mount" :
22 | {
23 | "dir" : "src/test/resources/mount",
24 | "uri" : "/mount-exclude",
25 | "excludes" :
26 | [
27 | "*.response"
28 | ]
29 | }
30 | }
31 | ]
32 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/extractor/CookieRequestExtractor.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.extractor;
2 |
3 | import com.github.dreamhead.moco.RequestExtractor;
4 | import com.github.dreamhead.moco.util.Cookies;
5 | import io.netty.handler.codec.http.FullHttpRequest;
6 |
7 | public class CookieRequestExtractor implements RequestExtractor {
8 | private final Cookies cookies = new Cookies();
9 |
10 | private final String key;
11 |
12 | public CookieRequestExtractor(String key) {
13 | this.key = key;
14 | }
15 |
16 | @Override
17 | public String extract(FullHttpRequest request) {
18 | return cookies.decodeCookie(request.headers().get("Cookie"), key);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/MocoMulipleConfigurationsTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import org.junit.Test;
4 |
5 | import java.io.IOException;
6 |
7 | import static com.github.dreamhead.moco.RemoteTestUtils.remoteUrl;
8 | import static org.hamcrest.CoreMatchers.is;
9 | import static org.junit.Assert.assertThat;
10 |
11 | public class MocoMulipleConfigurationsTest extends AbstractMocoStandaloneTest {
12 | @Test
13 | public void should_run_with_many_configurations() throws IOException {
14 | runWithConfiguration("multiple/foo.json", "multiple/bar.json");
15 | assertThat(helper.get(remoteUrl("/foo")), is("foo"));
16 | assertThat(helper.get(remoteUrl("/bar")), is("bar"));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/parser/model/GlobalSetting.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.parser.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 |
5 | import java.io.File;
6 |
7 | public class GlobalSetting {
8 | private String include;
9 | private String context;
10 | @JsonProperty("file_root")
11 | private String fileRoot;
12 | private String env;
13 |
14 | public String getInclude() {
15 | return fileRoot != null ? new File(fileRoot, include).getPath() : include;
16 | }
17 |
18 | public String getContext() {
19 | return context;
20 | }
21 |
22 | public String getFileRoot() {
23 | return fileRoot;
24 | }
25 |
26 | public String getEnv() {
27 | return env;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/handler/StatusCodeResponseHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.handler;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 | import com.github.dreamhead.moco.ResponseHandler;
5 | import io.netty.handler.codec.http.*;
6 |
7 | public class StatusCodeResponseHandler implements ResponseHandler {
8 | private final HttpResponseStatus status;
9 |
10 | public StatusCodeResponseHandler(int code) {
11 | status = HttpResponseStatus.valueOf(code);
12 | }
13 |
14 | @Override
15 | public void writeToResponse(FullHttpRequest request, FullHttpResponse response) {
16 | response.setStatus(status);
17 | }
18 |
19 | @Override
20 | public ResponseHandler apply(final MocoConfig config) {
21 | return this;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/handler/failover/Failover.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.handler.failover;
2 |
3 | import io.netty.handler.codec.http.FullHttpRequest;
4 | import io.netty.handler.codec.http.FullHttpResponse;
5 |
6 | public interface Failover {
7 | void onCompleteResponse(FullHttpRequest request, FullHttpResponse response);
8 | void failover(FullHttpRequest request, FullHttpResponse response);
9 |
10 | Failover EMPTY_FAILOVER = new Failover() {
11 | @Override
12 | public void onCompleteResponse(FullHttpRequest request, FullHttpResponse response) {
13 | }
14 |
15 | @Override
16 | public void failover(FullHttpRequest request, FullHttpResponse response) {
17 | throw new RuntimeException("no failover response found");
18 | }
19 | };
20 | }
21 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/extractor/ParamRequestExtractor.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.extractor;
2 |
3 | import com.github.dreamhead.moco.RequestExtractor;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 | import io.netty.handler.codec.http.QueryStringDecoder;
6 |
7 | import java.util.List;
8 |
9 | public class ParamRequestExtractor implements RequestExtractor {
10 | private final String param;
11 |
12 | public ParamRequestExtractor(String param) {
13 | this.param = param;
14 | }
15 |
16 | @Override
17 | public String extract(FullHttpRequest request) {
18 | QueryStringDecoder decoder = new QueryStringDecoder(request.getUri());
19 | List values = decoder.parameters().get(param);
20 | return values == null ? null : values.get(0);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/parser/model/ProxyContainer.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.parser.model;
2 |
3 | import com.google.common.base.Objects;
4 |
5 | public class ProxyContainer {
6 | private final String url;
7 | private final String failover;
8 |
9 | public ProxyContainer(String url, String failover) {
10 | this.url = url;
11 | this.failover = failover;
12 | }
13 |
14 | public String getUrl() {
15 | return url;
16 | }
17 |
18 | public String getFailover() {
19 | return failover;
20 | }
21 |
22 | @Override
23 | public String toString() {
24 | return Objects.toStringHelper(ProxyContainer.class)
25 | .add("url", this.url)
26 | .add("failover", this.failover)
27 | .toString();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/proxy.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request" :
4 | {
5 | "uri" : "/target"
6 | },
7 | "response" :
8 | {
9 | "text" : "proxy_target"
10 | }
11 | },
12 | {
13 | "request" :
14 | {
15 | "uri" : "/proxy"
16 | },
17 | "response" :
18 | {
19 | "proxy" : "http://localhost:12306/target"
20 | }
21 | },
22 | {
23 | "request" :
24 | {
25 | "uri" : "/failover"
26 | },
27 | "response" :
28 | {
29 | "proxy" :
30 | {
31 | "url" : "http://localhost:12306/unknown",
32 | "failover" : "src/test/resources/standalone_failover.json"
33 | }
34 | }
35 | }
36 |
37 | ]
38 |
39 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/model/Session.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.model;
2 |
3 | public class Session {
4 | private DefaultRequest request;
5 | private Response response;
6 |
7 | public DefaultRequest getRequest() {
8 | return request;
9 | }
10 |
11 | public void setRequest(DefaultRequest request) {
12 | this.request = request;
13 | }
14 |
15 | public Response getResponse() {
16 | return response;
17 | }
18 |
19 | public void setResponse(Response response) {
20 | this.response = response;
21 | }
22 |
23 | public static Session newSession(DefaultRequest request, Response response) {
24 | Session session = new Session();
25 | session.setRequest(request);
26 | session.setResponse(response);
27 | return session;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/util/FileContentTypeTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.util;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.hamcrest.CoreMatchers.is;
6 | import static org.junit.Assert.assertThat;
7 |
8 | public class FileContentTypeTest {
9 | private final String DEFAULT_CONTENT_TYPE = "text/plain; charset=UTF-8";
10 | private final String PNG_CONTENT_TYPE = "image/png";
11 |
12 | @Test
13 | public void should_get_type_from_filename() {
14 | FileContentType contentType = new FileContentType("logo.png");
15 | assertThat(contentType.getContentType(), is(PNG_CONTENT_TYPE));
16 | }
17 |
18 | @Test
19 | public void should_get_default_type_from_unknown_name() {
20 | FileContentType contentType = new FileContentType("UNKNOWN_FILE");
21 | assertThat(contentType.getContentType(), is(DEFAULT_CONTENT_TYPE));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/mount/MountTo.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.mount;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 |
5 | import static com.google.common.base.Strings.nullToEmpty;
6 |
7 | public class MountTo {
8 | private final String target;
9 |
10 | public MountTo(String target) {
11 | if (!target.endsWith("/")) {
12 | this.target = target + "/";
13 | } else {
14 | this.target = target;
15 | }
16 | }
17 |
18 | public String extract(String uri) {
19 | return uri.startsWith(this.target) ? nullToEmpty(uri.replaceFirst(this.target, "")) : "";
20 | }
21 |
22 | public MountTo apply(MocoConfig config) {
23 | if (config.isFor("uri")) {
24 | return new MountTo(config.apply(this.target));
25 | }
26 |
27 | return null;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/MocoDefenseTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import org.apache.http.client.HttpResponseException;
4 | import org.junit.Test;
5 |
6 | import static com.github.dreamhead.moco.Moco.*;
7 | import static com.github.dreamhead.moco.RemoteTestUtils.root;
8 | import static com.github.dreamhead.moco.Runner.running;
9 |
10 | public class MocoDefenseTest extends AbstractMocoTest {
11 | @Test(expected = HttpResponseException.class)
12 | public void should_work_well_without_response_setting() throws Exception {
13 | server = httpserver(12306, context("/foo"));
14 | server.request(by("bar"));
15 |
16 | running(server, new Runnable() {
17 | @Override
18 | public void run() throws Exception {
19 | helper.postContent(root(), "bar");
20 | }
21 | });
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/runner/RunnerSetting.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner;
2 |
3 | import com.google.common.base.Optional;
4 |
5 | import java.io.InputStream;
6 |
7 | public class RunnerSetting {
8 | private InputStream stream;
9 | private final Optional context;
10 | private final Optional fileRoot;
11 |
12 | public RunnerSetting(InputStream stream, String context, String fileRoot) {
13 | this.stream = stream;
14 | this.context = Optional.fromNullable(context);
15 | this.fileRoot = Optional.fromNullable(fileRoot);
16 | }
17 |
18 | public InputStream getStream() {
19 | return stream;
20 | }
21 |
22 | public Optional getContext() {
23 | return context;
24 | }
25 |
26 | public Optional getFileRoot() {
27 | return fileRoot;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/MocoProxyStandaloneTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import org.junit.Test;
4 |
5 | import java.io.IOException;
6 |
7 | import static com.github.dreamhead.moco.RemoteTestUtils.remoteUrl;
8 | import static org.hamcrest.CoreMatchers.is;
9 | import static org.junit.Assert.assertThat;
10 |
11 | public class MocoProxyStandaloneTest extends AbstractMocoStandaloneTest {
12 | @Test
13 | public void should_response_with_proxy() throws IOException {
14 | runWithConfiguration("proxy.json");
15 | String content = helper.get(remoteUrl("/proxy"));
16 | assertThat(content, is("proxy_target"));
17 | }
18 |
19 | @Test
20 | public void should_failover() throws IOException {
21 | runWithConfiguration("proxy.json");
22 | String content = helper.postContent(remoteUrl("/failover"), "proxy");
23 | assertThat(content, is("proxy"));
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/dumper/HttpMessageBaseDumper.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.dumper;
2 |
3 | import com.google.common.base.Joiner;
4 | import io.netty.handler.codec.http.FullHttpMessage;
5 | import io.netty.handler.codec.http.HttpHeaders;
6 | import io.netty.util.internal.StringUtil;
7 |
8 | import java.nio.charset.Charset;
9 |
10 | public abstract class HttpMessageBaseDumper implements Dumper {
11 | protected final Joiner.MapJoiner headerJoiner = Joiner.on(StringUtil.NEWLINE).withKeyValueSeparator(": ");
12 |
13 | protected static void appendContent(FullHttpMessage message, StringBuilder buf) {
14 | long contentLength = HttpHeaders.getContentLength(message, -1);
15 | if (contentLength > 0) {
16 | buf.append(StringUtil.NEWLINE);
17 | buf.append(StringUtil.NEWLINE);
18 | buf.append(message.content().toString(Charset.defaultCharset()));
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/handler/LatencyResponseHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.handler;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 | import com.github.dreamhead.moco.ResponseHandler;
5 | import io.netty.handler.codec.http.FullHttpRequest;
6 | import io.netty.handler.codec.http.FullHttpResponse;
7 |
8 | public class LatencyResponseHandler implements ResponseHandler {
9 | private final long millis;
10 |
11 | public LatencyResponseHandler(long millis) {
12 | this.millis = millis;
13 | }
14 |
15 | @Override
16 | public void writeToResponse(FullHttpRequest request, FullHttpResponse response) {
17 | try {
18 | Thread.sleep(millis);
19 | } catch (InterruptedException e) {
20 | throw new RuntimeException(e);
21 | }
22 | }
23 |
24 | @Override
25 | public ResponseHandler apply(final MocoConfig config) {
26 | return this;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/ShutdownArgs.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.bootstrap;
2 |
3 | import org.apache.commons.cli.*;
4 |
5 | public class ShutdownArgs extends ShutdownPortOption {
6 | public ShutdownArgs(Integer shutdownPort) {
7 | super(shutdownPort);
8 | }
9 |
10 | public static ShutdownArgs parse(String[] args) {
11 | try {
12 | CommandLineParser parser = new PosixParser();
13 | CommandLine cmd = parser.parse(createShutdownOptions(), args);
14 | return new ShutdownArgs(getShutdownPort(cmd.getOptionValue("s")));
15 | } catch (ParseException e) {
16 | throw new ParseArgException("fail to parse arguments", e);
17 | }
18 | }
19 |
20 | private static Options createShutdownOptions() {
21 | Options options = new Options();
22 | options.addOption(shutdownPortOption());
23 | return options;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/parser/model/TextContainer.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.parser.model;
2 |
3 | import com.google.common.base.Objects;
4 |
5 | public class TextContainer {
6 | private final String text;
7 | private final String operation;
8 |
9 | public TextContainer(String text, String operation) {
10 | this.text = text;
11 | this.operation = operation;
12 | }
13 |
14 | public boolean isRawText() {
15 | return this.operation == null;
16 | }
17 |
18 | public String getText() {
19 | return text;
20 | }
21 |
22 | public String getOperation() {
23 | return operation;
24 | }
25 |
26 | @Override
27 | public String toString() {
28 | return Objects.toStringHelper(this)
29 | .omitNullValues()
30 | .add("text", text)
31 | .add("operation", operation)
32 | .toString();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/resource/reader/FileResourceReader.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.resource.reader;
2 |
3 | import com.github.dreamhead.moco.util.FileContentType;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 |
6 | import java.io.File;
7 | import java.io.IOException;
8 |
9 | import static com.google.common.io.Files.toByteArray;
10 |
11 | public class FileResourceReader implements ContentResourceReader {
12 | private File file;
13 |
14 | public FileResourceReader(File file) {
15 | this.file = file;
16 | }
17 |
18 | @Override
19 | public String getContentType() {
20 | return new FileContentType(file.getName()).getContentType();
21 | }
22 |
23 | @Override
24 | public byte[] readFor(FullHttpRequest request) {
25 | try {
26 | return toByteArray(file);
27 | } catch (IOException e) {
28 | throw new RuntimeException(e);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/mount/MountToTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.mount;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.hamcrest.CoreMatchers.is;
6 | import static org.hamcrest.Matchers.isEmptyOrNullString;
7 | import static org.junit.Assert.assertThat;
8 |
9 | public class MountToTest {
10 |
11 | @Test
12 | public void should_get_relative_path_from_uri() {
13 | MountTo to = new MountTo("/dir");
14 | assertThat(to.extract("/dir/filename"), is("filename"));
15 | }
16 |
17 | @Test
18 | public void should_return_null_if_uri_does_not_match() {
19 | MountTo to = new MountTo("/dir");
20 | assertThat(to.extract("/target/filename"), isEmptyOrNullString());
21 | }
22 |
23 | @Test
24 | public void should_return_null_if_no_relative_path_found() {
25 | MountTo to = new MountTo("/dir");
26 | assertThat(to.extract("/dir/"), isEmptyOrNullString());
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/dumper/HttpResponseDumper.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.dumper;
2 |
3 | import io.netty.handler.codec.http.FullHttpResponse;
4 | import io.netty.handler.codec.http.HttpResponse;
5 | import io.netty.util.internal.StringUtil;
6 |
7 | public class HttpResponseDumper extends HttpMessageBaseDumper {
8 | @Override
9 | public String dump(FullHttpResponse response) {
10 | StringBuilder buf = new StringBuilder();
11 | appendResponseProtocolLine(response, buf);
12 | buf.append(StringUtil.NEWLINE);
13 | headerJoiner.appendTo(buf, response.headers());
14 | appendContent(response, buf);
15 |
16 | return buf.toString();
17 | }
18 |
19 | private void appendResponseProtocolLine(HttpResponse response, StringBuilder buf) {
20 | buf.append(response.getProtocolVersion().text());
21 | buf.append(' ');
22 | buf.append(response.getStatus().toString());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/runner/StandaloneRunner.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner;
2 |
3 | import com.github.dreamhead.moco.HttpServer;
4 | import com.github.dreamhead.moco.internal.ActualHttpServer;
5 | import com.github.dreamhead.moco.internal.MocoHttpServer;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | public class StandaloneRunner {
10 | private static Logger logger = LoggerFactory.getLogger(StandaloneRunner.class);
11 |
12 | private MocoHttpServer server;
13 |
14 | public void run(HttpServer httpServer) {
15 | ActualHttpServer actualHttpServer = (ActualHttpServer) httpServer;
16 | server = new MocoHttpServer(actualHttpServer);
17 | logger.info("Server is started at {}", actualHttpServer.getPort());
18 | server.start();
19 | }
20 |
21 | public void stop() {
22 | if (server != null) {
23 | server.stop();
24 | logger.info("Server stopped.");
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/dumper/HttpRequestDumper.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.dumper;
2 |
3 | import io.netty.handler.codec.http.FullHttpRequest;
4 | import io.netty.handler.codec.http.HttpRequest;
5 | import io.netty.util.internal.StringUtil;
6 |
7 | public class HttpRequestDumper extends HttpMessageBaseDumper {
8 | public String dump(FullHttpRequest request) {
9 | StringBuilder buf = new StringBuilder();
10 | appendRequestProtocolLine(request, buf);
11 | buf.append(StringUtil.NEWLINE);
12 | headerJoiner.appendTo(buf, request.headers());
13 | appendContent(request, buf);
14 |
15 | return buf.toString();
16 | }
17 |
18 | private void appendRequestProtocolLine(HttpRequest request, StringBuilder buf) {
19 | buf.append(request.getMethod().toString());
20 | buf.append(' ');
21 | buf.append(request.getUri());
22 | buf.append(' ');
23 | buf.append(request.getProtocolVersion().text());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/matcher/MatchMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.matcher;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 | import com.github.dreamhead.moco.RequestExtractor;
5 | import com.github.dreamhead.moco.RequestMatcher;
6 | import com.github.dreamhead.moco.resource.Resource;
7 | import com.google.common.base.Predicate;
8 |
9 | import java.util.regex.Pattern;
10 |
11 | public class MatchMatcher extends AbstractOperatorMatcher {
12 | public MatchMatcher(final RequestExtractor extractor, final Resource expected) {
13 | super(extractor, new Predicate() {
14 |
15 | @Override
16 | public boolean apply(String input) {
17 | Pattern pattern = Pattern.compile(new String(expected.readFor(null)));
18 | return pattern.matcher(input).matches();
19 | }
20 | });
21 | }
22 |
23 | @Override
24 | public RequestMatcher apply(final MocoConfig config) {
25 | return this;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/MocoJsonStandaloneTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import org.junit.Test;
4 |
5 | import java.io.IOException;
6 |
7 | import static com.github.dreamhead.moco.RemoteTestUtils.remoteUrl;
8 | import static org.hamcrest.CoreMatchers.is;
9 | import static org.junit.Assert.assertThat;
10 |
11 | public class MocoJsonStandaloneTest extends AbstractMocoStandaloneTest {
12 | @Test
13 | public void should_return_expected_response_based_on_specified_xml_request() throws IOException {
14 | runWithConfiguration("json.json");
15 | assertThat(helper.postContent(remoteUrl("/json"), "{\n\t\"foo\":\"bar\"\n}"), is("response_for_json_request"));
16 | }
17 |
18 | @Test
19 | public void should_return_expected_reponse_based_on_json_path_request() throws IOException {
20 | runWithConfiguration("jsonpath.json");
21 | assertThat(helper.postContent(remoteUrl("/jsonpath"), "{\"book\":[{\"price\":\"1\"}]}"), is("response_for_json_path_request"));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/bootstrap/ShutdownArgsTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.bootstrap;
2 |
3 | import static com.github.dreamhead.moco.bootstrap.ShutdownArgs.parse;
4 | import static org.hamcrest.CoreMatchers.is;
5 | import static org.junit.Assert.*;
6 |
7 | import org.junit.Test;
8 |
9 | public class ShutdownArgsTest {
10 |
11 | private final int DEFAULT_SHUTDOWN_PORT = 9527;
12 |
13 | @Test
14 | public void should_parse_shutdown_arguments() {
15 | ShutdownArgs args = parse(new String[] { "shutdown", "-s", "12305" });
16 | assertThat(12305, is(args.getShutdownPort(DEFAULT_SHUTDOWN_PORT)));
17 | }
18 |
19 | @Test
20 | public void should_parse_shutdown_default_arguments() {
21 | ShutdownArgs args = parse(new String[] { "shutdown" });
22 | assertThat(DEFAULT_SHUTDOWN_PORT,
23 | is(args.getShutdownPort(DEFAULT_SHUTDOWN_PORT)));
24 | }
25 |
26 | @Test(expected = ParseArgException.class)
27 | public void should_set_shutdown_port() {
28 | parse(new String[] { "shutdown", "-s" });
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/resource/reader/ClasspathFileResourceReader.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.resource.reader;
2 |
3 | import com.github.dreamhead.moco.util.FileContentType;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 |
6 | import java.io.IOException;
7 |
8 | import static com.google.common.io.ByteStreams.toByteArray;
9 |
10 | public class ClasspathFileResourceReader implements ContentResourceReader {
11 | private String filename;
12 |
13 | public ClasspathFileResourceReader(String filename) {
14 | this.filename = filename;
15 | }
16 |
17 | @Override
18 | public byte[] readFor(FullHttpRequest request) {
19 | try {
20 | return toByteArray(this.getClass().getClassLoader().getResourceAsStream(filename));
21 | } catch (IOException e) {
22 | throw new RuntimeException(e);
23 | }
24 | }
25 |
26 | @Override
27 | public String getContentType() {
28 | return new FileContentType(this.filename).getContentType();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/handler/VersionResponseHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.handler;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 | import com.github.dreamhead.moco.ResponseHandler;
5 | import com.github.dreamhead.moco.resource.Resource;
6 | import io.netty.handler.codec.http.FullHttpRequest;
7 | import io.netty.handler.codec.http.FullHttpResponse;
8 | import io.netty.handler.codec.http.HttpVersion;
9 |
10 | public class VersionResponseHandler implements ResponseHandler {
11 | private final Resource resource;
12 |
13 | public VersionResponseHandler(Resource resource) {
14 | this.resource = resource;
15 | }
16 |
17 | @Override
18 | public void writeToResponse(FullHttpRequest request, FullHttpResponse response) {
19 | HttpVersion httpVersion = HttpVersion.valueOf(new String(resource.readFor(request)));
20 | response.setProtocolVersion(httpVersion);
21 | }
22 |
23 | @Override
24 | public ResponseHandler apply(final MocoConfig config) {
25 | return this;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/ShutdownPortOption.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.bootstrap;
2 |
3 | import com.google.common.base.Optional;
4 | import org.apache.commons.cli.Option;
5 |
6 | public class ShutdownPortOption {
7 | private Optional shutdownPort;
8 |
9 | public ShutdownPortOption(Integer shutdownPort) {
10 | this.shutdownPort = Optional.fromNullable(shutdownPort);
11 | }
12 |
13 | public boolean hasShutdonwPort() {
14 | return shutdownPort.isPresent();
15 | }
16 |
17 | public int getShutdownPort(int defaultShutDownPort) {
18 | return shutdownPort.or(defaultShutDownPort);
19 | }
20 |
21 | public static Option shutdownPortOption() {
22 | Option opt = new Option("s", true, "shutdown port");
23 | opt.setType(String.class);
24 | opt.setRequired(false);
25 | return opt;
26 | }
27 |
28 | public static Integer getShutdownPort(String shutdownPort) {
29 | return shutdownPort == null ? null : Integer.valueOf(shutdownPort);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/matcher/AndRequestMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.matcher;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 | import com.github.dreamhead.moco.RequestMatcher;
5 | import io.netty.handler.codec.http.FullHttpRequest;
6 |
7 | public class AndRequestMatcher extends CompositeRequestMatcher {
8 | public AndRequestMatcher(Iterable matchers) {
9 | super(matchers);
10 | }
11 |
12 | @Override
13 | public boolean match(FullHttpRequest request) {
14 | for (RequestMatcher matcher : matchers) {
15 | if (!matcher.match(request)) {
16 | return false;
17 | }
18 | }
19 |
20 | return true;
21 | }
22 |
23 | @Override
24 | public RequestMatcher apply(final MocoConfig config) {
25 | Iterable appliedMatchers = applyToMatchers(config);
26 | if (appliedMatchers == this.matchers) {
27 | return this;
28 | }
29 |
30 | return new AndRequestMatcher(appliedMatchers);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/matcher/OrRequestMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.matcher;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 | import com.github.dreamhead.moco.RequestMatcher;
5 | import io.netty.handler.codec.http.FullHttpRequest;
6 |
7 | public class OrRequestMatcher extends CompositeRequestMatcher {
8 | public OrRequestMatcher(Iterable matchers) {
9 | super(matchers);
10 | }
11 |
12 | @Override
13 | public boolean match(FullHttpRequest request) {
14 | for (RequestMatcher matcher : matchers) {
15 | if (matcher.match(request)) {
16 | return true;
17 | }
18 | }
19 |
20 | return false;
21 | }
22 |
23 | @Override
24 | public RequestMatcher apply(final MocoConfig config) {
25 | Iterable appliedMatchers = applyToMatchers(config);
26 | if (appliedMatchers == this.matchers) {
27 | return this;
28 | }
29 |
30 | return new OrRequestMatcher(applyToMatchers(config));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/extractor/JsonPathRequestExtractor.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.extractor;
2 |
3 | import com.github.dreamhead.moco.RequestExtractor;
4 | import com.jayway.jsonpath.JsonPath;
5 | import io.netty.handler.codec.http.FullHttpRequest;
6 |
7 | import java.util.List;
8 |
9 | public class JsonPathRequestExtractor implements RequestExtractor {
10 | private final ContentRequestExtractor extractor = new ContentRequestExtractor();
11 | private final JsonPath jsonPath;
12 |
13 | public JsonPathRequestExtractor(String jsonPath) {
14 | this.jsonPath = JsonPath.compile(jsonPath);
15 | }
16 |
17 | @Override
18 | public String[] extract(FullHttpRequest request) {
19 | return toStringArray(jsonPath.read(extractor.extract(request)));
20 | }
21 |
22 | @SuppressWarnings("unchecked")
23 | private String[] toStringArray(Object content){
24 | if(content instanceof List){
25 | List texts = (List) content;
26 | return texts.toArray(new String[texts.size()]);
27 | }
28 |
29 | return new String[]{content.toString()};
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/extractor/Extractors.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.extractor;
2 |
3 | import com.github.dreamhead.moco.RequestExtractor;
4 | import com.google.common.collect.ImmutableMap;
5 |
6 | import static java.lang.String.format;
7 |
8 | public class Extractors {
9 | private static ImmutableMap> extractors = ImmutableMap.>builder()
10 | .put("file", new ContentRequestExtractor())
11 | .put("text", new ContentRequestExtractor())
12 | .put("pathresource", new ContentRequestExtractor())
13 | .put("uri", new UriRequestExtractor())
14 | .put("method", new HttpMethodExtractor())
15 | .put("version", new VersionExtractor()).build();
16 |
17 | public static RequestExtractor extractor(String id) {
18 | if (extractors.containsKey(id)) {
19 | return extractors.get(id);
20 | }
21 |
22 | throw new IllegalArgumentException(format("unknown extractor for [%s]", id));
23 | }
24 |
25 | private Extractors() {}
26 | }
27 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/handler/ContentHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.handler;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 | import com.github.dreamhead.moco.ResponseHandler;
5 | import com.github.dreamhead.moco.resource.ContentResource;
6 | import io.netty.buffer.ByteBuf;
7 | import io.netty.handler.codec.http.FullHttpRequest;
8 |
9 | public class ContentHandler extends AbstractContentResponseHandler {
10 | private final ContentResource resource;
11 |
12 | public ContentHandler(final ContentResource resource) {
13 | this.resource = resource;
14 | }
15 |
16 | @Override
17 | protected void writeContentResponse(FullHttpRequest request, ByteBuf buffer) {
18 | buffer.writeBytes(this.resource.readFor(request));
19 | }
20 |
21 | @Override
22 | protected String getContentType(FullHttpRequest request) {
23 | return resource.getContentType();
24 | }
25 |
26 | @Override
27 | public ResponseHandler apply(final MocoConfig config) {
28 | return new ContentHandler((ContentResource)this.resource.apply(config));
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/resource/Resource.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.resource;
2 |
3 | import com.github.dreamhead.moco.ConfigApplier;
4 | import com.github.dreamhead.moco.MocoConfig;
5 | import io.netty.handler.codec.http.FullHttpRequest;
6 |
7 | public class Resource implements Identifiable, ConfigApplier, ResourceReader {
8 | private Identifiable identifiable;
9 | private ResourceConfigApplier configApplier;
10 | protected ResourceReader reader;
11 |
12 | public Resource(Identifiable identifiable, ResourceConfigApplier configApplier, ResourceReader reader) {
13 | this.identifiable = identifiable;
14 | this.configApplier = configApplier;
15 | this.reader = reader;
16 | }
17 |
18 | @Override
19 | public Resource apply(MocoConfig config) {
20 | return configApplier.apply(config, this);
21 | }
22 |
23 | @Override
24 | public String id() {
25 | return identifiable.id();
26 | }
27 |
28 | @Override
29 | public byte[] readFor(FullHttpRequest request) {
30 | return reader.readFor(request);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/MIT-LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2013 ZHENG Ye
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining
5 | a copy of this software and associated documentation files (the
6 | "Software"), to deal in the Software without restriction, including
7 | without limitation the rights to use, copy, modify, merge, publish,
8 | distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to
10 | the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/MocoMount.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import com.github.dreamhead.moco.mount.MountPredicate;
4 | import com.github.dreamhead.moco.mount.MountTo;
5 |
6 | import static org.apache.commons.io.FilenameUtils.wildcardMatch;
7 |
8 | public class MocoMount {
9 | public static MountTo to(String target) {
10 | return new MountTo(target);
11 | }
12 |
13 | public static MountPredicate include(final String wildcard) {
14 | return new MountPredicate() {
15 | @Override
16 | public boolean apply(String filename) {
17 | return wildcardMatch(filename, wildcard);
18 | }
19 | };
20 | }
21 |
22 | public static MountPredicate exclude(String wildcard) {
23 | return not(include(wildcard));
24 | }
25 |
26 | private static MountPredicate not(final MountPredicate predicate) {
27 | return new MountPredicate() {
28 | @Override
29 | public boolean apply(String filename) {
30 | return !predicate.apply(filename);
31 | }
32 | };
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/runner/monitor/FilesMonitor.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner.monitor;
2 |
3 | import com.google.common.base.Function;
4 | import org.apache.commons.io.monitor.FileAlterationListener;
5 |
6 | import java.io.File;
7 |
8 | import static com.google.common.collect.FluentIterable.from;
9 |
10 | public class FilesMonitor implements Monitor {
11 | private final Iterable monitors;
12 |
13 | public FilesMonitor(Iterable files, final FileAlterationListener listener) {
14 | this.monitors = from(files).transform(new Function() {
15 | @Override
16 | public FileMonitor apply(File file) {
17 | return new FileMonitor(file, listener);
18 | }
19 | });
20 | }
21 |
22 | @Override
23 | public void startMonitor() {
24 | for (FileMonitor monitor : monitors) {
25 | monitor.startMonitor();
26 | }
27 | }
28 |
29 | @Override
30 | public void stopMonitor() {
31 | for (FileMonitor monitor : monitors) {
32 | monitor.stopMonitor();
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/internal/MocoClient.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.internal;
2 |
3 | import io.netty.bootstrap.Bootstrap;
4 | import io.netty.channel.*;
5 | import io.netty.channel.nio.NioEventLoopGroup;
6 | import io.netty.channel.socket.nio.NioSocketChannel;
7 |
8 | public class MocoClient {
9 | public void run(final String host, final int port, final ChannelHandler pipelineFactory) {
10 | EventLoopGroup group = new NioEventLoopGroup();
11 | Bootstrap bootstrap = new Bootstrap();
12 | bootstrap.group(group)
13 | .channel(NioSocketChannel.class)
14 | .remoteAddress(host, port)
15 | .option(ChannelOption.TCP_NODELAY, true)
16 | .handler(pipelineFactory);
17 |
18 | try {
19 | Channel channel = bootstrap.connect().sync().channel();
20 | ChannelFuture future = channel.closeFuture().sync();
21 | future.addListener(ChannelFutureListener.CLOSE);
22 | } catch (InterruptedException e) {
23 | throw new RuntimeException(e);
24 | } finally {
25 | group.shutdownGracefully();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/extractor/UriRequestExtractorTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.extractor;
2 |
3 | import io.netty.handler.codec.http.FullHttpRequest;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 |
7 | import static org.hamcrest.CoreMatchers.is;
8 | import static org.junit.Assert.assertThat;
9 | import static org.mockito.Mockito.mock;
10 | import static org.mockito.Mockito.when;
11 |
12 | public class UriRequestExtractorTest {
13 | private UriRequestExtractor extractor;
14 | private FullHttpRequest request;
15 |
16 | @Before
17 | public void setUp() throws Exception {
18 | extractor = new UriRequestExtractor();
19 | request = mock(FullHttpRequest.class);
20 | }
21 |
22 | @Test
23 | public void should_return_all_uri_while_uri_has_no_parameter() {
24 | when(request.getUri()).thenReturn("/foo");
25 | assertThat(extractor.extract(request), is("/foo"));
26 | }
27 |
28 | @Test
29 | public void should_return_path_part_while_uri_has_parameters() {
30 | when(request.getUri()).thenReturn("/foo?param=bar");
31 | assertThat(extractor.extract(request), is("/foo"));
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/moco-core/src/test/resources/many_content_failover.response:
--------------------------------------------------------------------------------
1 | [ {
2 | "request" : {
3 | "version" : "HTTP/1.1",
4 | "content" : "",
5 | "headers" : {
6 | "Host" : "localhost:12306",
7 | "User-Agent" : "Apache-HttpClient/4.2.5 (java 1.5)",
8 | "Connection" : "Keep-Alive"
9 | },
10 | "queries" : null,
11 | "method" : "GET"
12 | },
13 | "response" : {
14 | "version" : "HTTP/1.1",
15 | "content" : "get_proxy",
16 | "headers" : {
17 | "Content-Length" : "9",
18 | "Content-Type" : "text/plain; charset=UTF-8"
19 | },
20 | "statusCode" : 200
21 | }
22 | }, {
23 | "request" : {
24 | "version" : "HTTP/1.1",
25 | "content" : "proxy",
26 | "headers" : {
27 | "Host" : "localhost:12306",
28 | "Content-Length" : "5",
29 | "User-Agent" : "Apache-HttpClient/4.2.5 (java 1.5)",
30 | "Connection" : "Keep-Alive"
31 | },
32 | "queries" : null,
33 | "method" : "POST"
34 | },
35 | "response" : {
36 | "version" : "HTTP/1.1",
37 | "content" : "post_proxy",
38 | "headers" : {
39 | "Content-Length" : "10",
40 | "Content-Type" : "text/plain; charset=UTF-8"
41 | },
42 | "statusCode" : 200
43 | }
44 | } ]
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/MocoPathResourceTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import com.google.common.io.ByteStreams;
4 | import org.junit.Test;
5 |
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 |
9 | import static com.github.dreamhead.moco.RemoteTestUtils.remoteUrl;
10 | import static com.github.dreamhead.moco.RemoteTestUtils.root;
11 | import static org.hamcrest.CoreMatchers.is;
12 | import static org.junit.Assert.assertThat;
13 |
14 | public class MocoPathResourceTest extends AbstractMocoStandaloneTest {
15 | @Test
16 | public void should_return_path_resource_based_on_specified_request() throws IOException {
17 | runWithConfiguration("path_resource.json");
18 | assertThat(helper.get(remoteUrl("/path-resource")), is("response from path"));
19 | }
20 |
21 | @Test
22 | public void should_return_response_based_on_path_resource() throws IOException {
23 | runWithConfiguration("path_resource.json");
24 |
25 | InputStream asStream = this.getClass().getClassLoader().getResourceAsStream("path.request");
26 | assertThat(helper.postBytes(root(), ByteStreams.toByteArray(asStream)), is("path resource"));
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/matcher/EqRequestMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.matcher;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 | import com.github.dreamhead.moco.RequestExtractor;
5 | import com.github.dreamhead.moco.RequestMatcher;
6 | import com.github.dreamhead.moco.resource.Resource;
7 | import com.google.common.base.Predicate;
8 |
9 | import java.util.Arrays;
10 |
11 | public class EqRequestMatcher extends AbstractOperatorMatcher {
12 | private final Resource expected;
13 |
14 | public EqRequestMatcher(final RequestExtractor extractor, final Resource expected) {
15 | super(extractor, new Predicate() {
16 | @Override
17 | public boolean apply(String input) {
18 | return Arrays.equals(input.getBytes(), expected.readFor(null));
19 | }
20 | });
21 | this.expected = expected;
22 | }
23 |
24 | @Override
25 | public RequestMatcher apply(final MocoConfig config) {
26 | Resource appliedResource = expected.apply(config);
27 | if (appliedResource == expected) {
28 | return this;
29 | }
30 |
31 | return new EqRequestMatcher(extractor, appliedResource);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/handler/AbstractContentResponseHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.handler;
2 |
3 | import com.github.dreamhead.moco.ResponseHandler;
4 | import io.netty.buffer.ByteBuf;
5 | import io.netty.buffer.Unpooled;
6 | import io.netty.handler.codec.http.FullHttpRequest;
7 | import io.netty.handler.codec.http.FullHttpResponse;
8 |
9 | public abstract class AbstractContentResponseHandler implements ResponseHandler {
10 | private final ContentTypeDetector detector = new ContentTypeDetector();
11 |
12 | protected abstract void writeContentResponse(FullHttpRequest request, ByteBuf buffer);
13 |
14 | @Override
15 | public void writeToResponse(FullHttpRequest request, FullHttpResponse response) {
16 | ByteBuf buffer = Unpooled.buffer();
17 | writeContentResponse(request, buffer);
18 | response.content().writeBytes(buffer);
19 | response.headers().set("Content-Length", buffer.writerIndex());
20 | if (!detector.hasContentType(response)) {
21 | response.headers().set("Content-Type", getContentType(request));
22 | }
23 | }
24 |
25 | protected String getContentType(FullHttpRequest request) {
26 | return "text/html; charset=UTF-8";
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/extractor/VersionExtractorTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.extractor;
2 |
3 | import static org.hamcrest.CoreMatchers.is;
4 | import static org.junit.Assert.assertThat;
5 | import io.netty.handler.codec.http.DefaultFullHttpRequest;
6 | import io.netty.handler.codec.http.FullHttpRequest;
7 | import io.netty.handler.codec.http.HttpMethod;
8 | import io.netty.handler.codec.http.HttpVersion;
9 |
10 | import org.junit.Before;
11 | import org.junit.Test;
12 |
13 | public class VersionExtractorTest {
14 | private VersionExtractor extractor;
15 | private FullHttpRequest request;
16 |
17 | @Before
18 | public void setUp() {
19 | extractor = new VersionExtractor();
20 | }
21 |
22 | @Test
23 | public void shoud_get_right_version_with_HTTP_1_0() {
24 | request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_0,
25 | HttpMethod.GET, "/foo");
26 | assertThat(extractor.extract(request),
27 | is(HttpVersion.HTTP_1_0.toString()));
28 | }
29 |
30 | @Test
31 | public void shoud_get_right_version_with_HTTP_1_1() {
32 | request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,
33 | HttpMethod.GET, "/foo");
34 | assertThat(extractor.extract(request),
35 | is(HttpVersion.HTTP_1_1.toString()));
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/handler/HeaderResponseHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.handler;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 | import com.github.dreamhead.moco.ResponseHandler;
5 | import com.github.dreamhead.moco.resource.Resource;
6 | import io.netty.handler.codec.http.*;
7 |
8 | public class HeaderResponseHandler implements ResponseHandler {
9 | private final ContentTypeDetector detector = new ContentTypeDetector();
10 |
11 | private final String name;
12 | private final Resource resource;
13 |
14 | public HeaderResponseHandler(String name, Resource resource) {
15 | this.name = name;
16 | this.resource = resource;
17 | }
18 |
19 | @Override
20 | public void writeToResponse(FullHttpRequest request, FullHttpResponse response) {
21 | if (detector.hasHeader(response, name)) {
22 | response.headers().remove(name);
23 | }
24 |
25 | HttpHeaders.addHeader(response, name, new String(resource.readFor(request)));
26 | }
27 |
28 | @Override
29 | public ResponseHandler apply(final MocoConfig config) {
30 | if (config.isFor(resource.id())) {
31 | return new HeaderResponseHandler(name, resource.apply(config));
32 | }
33 |
34 | return this;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/internal/MocoEventListener.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.internal;
2 |
3 | import com.github.dreamhead.moco.dumper.Dumper;
4 | import com.github.dreamhead.moco.dumper.HttpRequestDumper;
5 | import com.github.dreamhead.moco.dumper.HttpResponseDumper;
6 | import com.google.common.eventbus.Subscribe;
7 | import io.netty.handler.codec.http.FullHttpRequest;
8 | import io.netty.handler.codec.http.FullHttpResponse;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | public class MocoEventListener {
13 | private static Logger logger = LoggerFactory.getLogger(MocoEventListener.class);
14 | private final Dumper requestDumper = new HttpRequestDumper();
15 | private final Dumper responseDumper = new HttpResponseDumper();
16 |
17 | @Subscribe
18 | public void onMessageArrived(FullHttpRequest request) {
19 | logger.info("Request received:\n\n{}\n", requestDumper.dump(request));
20 | }
21 |
22 | @Subscribe
23 | public void onException(Exception e) {
24 | logger.error("Exception thrown", e);
25 | }
26 |
27 | @Subscribe
28 | public void onMessageLeave(FullHttpResponse response) {
29 | logger.info("Response return:\n\n{}\n", responseDumper.dump(response));
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/parser/model/TextContainerDeserializer.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.parser.model;
2 |
3 | import com.fasterxml.jackson.core.JsonParser;
4 | import com.fasterxml.jackson.core.JsonToken;
5 | import com.fasterxml.jackson.databind.DeserializationContext;
6 | import com.fasterxml.jackson.databind.JsonDeserializer;
7 |
8 | import java.io.IOException;
9 |
10 | public class TextContainerDeserializer extends JsonDeserializer {
11 | @Override
12 | public TextContainer deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
13 | JsonToken currentToken = jp.getCurrentToken();
14 | if (currentToken == JsonToken.VALUE_STRING) {
15 | return new TextContainer(jp.getText().trim(), null);
16 | } else if (currentToken == JsonToken.START_OBJECT) {
17 | JsonToken jsonToken = jp.nextToken();
18 | if (jsonToken == JsonToken.FIELD_NAME) {
19 | String operation = jp.getText().trim();
20 | jp.nextToken();
21 | String text = jp.getText().trim();
22 | jp.nextToken();
23 | return new TextContainer(text, operation);
24 | }
25 | }
26 |
27 | throw ctxt.mappingException(TextContainer.class, currentToken);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/helper/MocoTestHelper.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.helper;
2 |
3 | import com.google.common.io.Resources;
4 | import org.apache.http.client.fluent.Content;
5 | import org.apache.http.client.fluent.Request;
6 |
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 |
10 | import static com.google.common.io.ByteStreams.toByteArray;
11 |
12 | public class MocoTestHelper {
13 | public String get(String uri) throws IOException {
14 | Content content = Request.Get(uri).execute().returnContent();
15 | return content.asString();
16 | }
17 |
18 | public String postContent(String uri, String postContent) throws IOException {
19 | return postBytes(uri, postContent.getBytes());
20 | }
21 |
22 | public String postBytes(String uri, byte[] bytes) throws IOException {
23 | Content content = Request.Post(uri).bodyByteArray(bytes)
24 | .execute().returnContent();
25 | return content.asString();
26 | }
27 |
28 | public String postFile(String uri, String file) throws IOException {
29 | InputStream is = Resources.getResource(file).openStream();
30 | Content content = Request.Post(uri).bodyByteArray(toByteArray(is))
31 | .execute().returnContent();
32 | return content.asString();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/moco-doc/ReleaseNote.md:
--------------------------------------------------------------------------------
1 | # Release 0.8.1 (1-August-2013)
2 | * Upgrade to Netty 4
3 | * Fixed proxy bug
4 |
5 | # Release 0.8 (21-July-2013)
6 |
7 | ## Usage
8 | * global setting file
9 |
10 | ## APIs
11 | ### Request
12 | * JSONPath
13 | ### Response
14 | #### Integration
15 | * proxy
16 | * with
17 | * (Beta) template
18 | * removed url (using proxy instead)
19 | * removed content (using with instead)
20 |
21 | #### Misc Response
22 | * removed cache
23 |
24 | ## Settings
25 | * context
26 | * file root
27 | * environment
28 |
29 | # Release 0.7 (1-May-2013)
30 |
31 | the first public release of Moco
32 |
33 | ## Usage
34 | * API
35 | * stanalone server
36 | * shell
37 | * maven plugin (external)
38 | * gradle plugin (external)
39 |
40 | ## APIs
41 |
42 | ### Request
43 |
44 | #### HTTP
45 | * content(text, file)
46 | * URI
47 | * query Parameter
48 | * HTTP method
49 | * version
50 | * header
51 | * redirect
52 |
53 | #### Operator
54 | * eq
55 | * match
56 |
57 | #### Web
58 | * cookie
59 | * form
60 |
61 | #### Integration
62 | * XML
63 | * XPath
64 | * JSON
65 |
66 | ### Response
67 |
68 | #### HTTP
69 | * content(text, file)
70 | * version
71 | * status code
72 | * header
73 |
74 | #### Web
75 | * cookie
76 |
77 | #### Integration
78 | * url
79 |
80 | #### Misc Response
81 | * latency
82 | * sequence(API only)
83 | * cache
84 |
85 | ### Misc
86 | * mount
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/matcher/CompositeRequestMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.matcher;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 | import com.github.dreamhead.moco.RequestMatcher;
5 | import com.google.common.base.Function;
6 | import com.google.common.collect.FluentIterable;
7 |
8 | import static com.google.common.collect.FluentIterable.from;
9 |
10 | public abstract class CompositeRequestMatcher implements RequestMatcher {
11 | protected final Iterable matchers;
12 |
13 | public CompositeRequestMatcher(Iterable matchers) {
14 | this.matchers = matchers;
15 | }
16 |
17 | protected Iterable applyToMatchers(MocoConfig config) {
18 | FluentIterable appliedMatchers = from(matchers).transform(applyConfig(config));
19 | if (matchers.equals(appliedMatchers)) {
20 | return this.matchers;
21 | }
22 |
23 | return appliedMatchers;
24 | }
25 |
26 | private Function applyConfig(final MocoConfig config) {
27 | return new Function() {
28 | @Override
29 | public RequestMatcher apply(RequestMatcher matcher) {
30 | return matcher.apply(config);
31 | }
32 | };
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/AbstractMocoStandaloneTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import com.github.dreamhead.moco.helper.MocoTestHelper;
4 | import com.github.dreamhead.moco.runner.JsonRunner;
5 | import com.google.common.io.Resources;
6 | import org.junit.After;
7 |
8 | import java.io.IOException;
9 | import java.io.InputStream;
10 | import java.util.List;
11 |
12 | import static com.github.dreamhead.moco.RemoteTestUtils.port;
13 | import static com.github.dreamhead.moco.runner.JsonRunner.newJsonRunnerWithStreams;
14 | import static com.google.common.collect.Lists.newArrayList;
15 |
16 | public class AbstractMocoStandaloneTest {
17 | protected final MocoTestHelper helper = new MocoTestHelper();
18 | private JsonRunner runner;
19 |
20 | @After
21 | public void teardown() {
22 | runner.stop();
23 | }
24 |
25 | protected void runWithConfiguration(String... resourceNames) {
26 | try {
27 | List streams = newArrayList();
28 | for (String resourceName : resourceNames) {
29 | streams.add(Resources.getResource(resourceName).openStream());
30 | }
31 | runner = newJsonRunnerWithStreams(streams, port());
32 | runner.run();
33 | } catch (IOException e) {
34 | throw new RuntimeException(e);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/util/Cookies.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.util;
2 |
3 | import com.google.common.base.Predicate;
4 | import io.netty.handler.codec.http.Cookie;
5 | import io.netty.handler.codec.http.CookieDecoder;
6 | import io.netty.handler.codec.http.DefaultCookie;
7 | import io.netty.handler.codec.http.ServerCookieEncoder;
8 |
9 | import java.util.Set;
10 |
11 | import static com.google.common.collect.Iterables.find;
12 |
13 | public class Cookies {
14 | public String encodeCookie(String key, String value) {
15 | Cookie cookie = new DefaultCookie(key, value);
16 | cookie.setPath("/");
17 | return ServerCookieEncoder.encode(cookie);
18 | }
19 |
20 | public String decodeCookie(String cookieString, String key) {
21 | if(cookieString == null) {
22 | return null;
23 | }
24 |
25 | Set cookies = CookieDecoder.decode(cookieString);
26 |
27 | Cookie cookie = find(cookies, byCookieName(key), null);
28 | return cookie == null ? null : cookie.getValue();
29 | }
30 |
31 | private static Predicate byCookieName(final String key) {
32 | return new Predicate() {
33 | @Override
34 | public boolean apply(Cookie cookie) {
35 | return key.equals(cookie.getName());
36 | }
37 | };
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/parser/GlobalSettingParser.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.parser;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
5 | import com.fasterxml.jackson.databind.type.TypeFactory;
6 | import com.github.dreamhead.moco.parser.model.GlobalSetting;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | import java.io.IOException;
11 | import java.io.InputStream;
12 | import java.util.List;
13 |
14 | import static java.lang.String.format;
15 |
16 | public class GlobalSettingParser {
17 | private static Logger logger = LoggerFactory.getLogger(GlobalSettingParser.class);
18 | private final ObjectMapper mapper = new ObjectMapper();
19 | private final TypeFactory factory = TypeFactory.defaultInstance();
20 |
21 | public List parse(InputStream is) {
22 | try {
23 | return mapper.readValue(is, factory.constructCollectionType(List.class, GlobalSetting.class));
24 | } catch (UnrecognizedPropertyException upe) {
25 | logger.info("Unrecognized field: {}", upe.getMessage());
26 | throw new RuntimeException(format("Unrecognized field [ %s ], please check!", upe.getUnrecognizedPropertyName()));
27 | } catch (IOException e) {
28 | throw new RuntimeException(e);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/runner/DynamicConfigurationRunnerTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner;
2 |
3 | import com.github.dreamhead.moco.bootstrap.StartArgs;
4 | import org.junit.Test;
5 |
6 | import java.io.File;
7 | import java.io.IOException;
8 |
9 | import static com.github.dreamhead.moco.RemoteTestUtils.port;
10 | import static com.github.dreamhead.moco.RemoteTestUtils.root;
11 | import static org.hamcrest.CoreMatchers.is;
12 | import static org.junit.Assert.assertThat;
13 |
14 | public class DynamicConfigurationRunnerTest extends AbstractRunnerTest {
15 | @Test
16 | public void should_reload_configuration() throws IOException, InterruptedException {
17 | final File config = File.createTempFile("config", ".json");
18 | changeFileContent(config, "[{\"response\" :{" +
19 | "\"text\" : \"foo\"" +
20 | "}}]");
21 |
22 | RunnerFactory factory = new RunnerFactory(9090, "SHUTDOWN");
23 | runner = factory.createRunner(new StartArgs(port(), 9090, config.getAbsolutePath(), null, null));
24 | runner.run();
25 | assertThat(helper.get(root()), is("foo"));
26 |
27 | changeFileContent(config, "[{\"response\" :{" +
28 | "\"text\" : \"foobar\"" +
29 | "}}]");
30 |
31 | waitChangeHappens();
32 |
33 | assertThat(helper.get(root()), is("foobar"));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/internal/MocoHttpServer.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.internal;
2 |
3 | import io.netty.channel.ChannelInitializer;
4 | import io.netty.channel.ChannelPipeline;
5 | import io.netty.channel.socket.SocketChannel;
6 | import io.netty.handler.codec.http.HttpObjectAggregator;
7 | import io.netty.handler.codec.http.HttpRequestDecoder;
8 | import io.netty.handler.codec.http.HttpResponseEncoder;
9 |
10 | public class MocoHttpServer {
11 | private final MocoServer server = new MocoServer();
12 | private final ActualHttpServer serverSetting;
13 |
14 | public MocoHttpServer(ActualHttpServer serverSetting) {
15 | this.serverSetting = serverSetting;
16 | }
17 |
18 | public void start() {
19 | server.start(serverSetting.getPort(), new ChannelInitializer() {
20 | @Override
21 | protected void initChannel(SocketChannel ch) throws Exception {
22 | ChannelPipeline pipeline = ch.pipeline();
23 | pipeline.addLast("decoder", new HttpRequestDecoder());
24 | pipeline.addLast("aggregator", new HttpObjectAggregator(1048576));
25 | pipeline.addLast("encoder", new HttpResponseEncoder());
26 | pipeline.addLast("handler", new MocoHandler(serverSetting));
27 | }
28 | });
29 | }
30 |
31 | public void stop() {
32 | server.stop();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/matcher/AbstractOperatorMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.matcher;
2 |
3 | import com.github.dreamhead.moco.RequestExtractor;
4 | import com.github.dreamhead.moco.RequestMatcher;
5 | import com.google.common.base.Predicate;
6 | import io.netty.handler.codec.http.FullHttpRequest;
7 |
8 | public abstract class AbstractOperatorMatcher implements RequestMatcher {
9 | protected final RequestExtractor extractor;
10 | private final Predicate predicate;
11 |
12 | protected AbstractOperatorMatcher(RequestExtractor extractor, Predicate predicate) {
13 | this.extractor = extractor;
14 | this.predicate = predicate;
15 | }
16 |
17 | @Override
18 | public boolean match(FullHttpRequest request) {
19 | T extractContent = extractor.extract(request);
20 | if (extractContent == null) {
21 | return false;
22 | }
23 |
24 | if (extractContent instanceof String) {
25 | return predicate.apply((String)extractContent);
26 | }
27 |
28 | if (extractContent instanceof String[]) {
29 | String[] contents = (String[])extractContent;
30 | for (String content : contents) {
31 | if (predicate.apply(content)) {
32 | return true;
33 | }
34 | }
35 |
36 | }
37 |
38 | return false;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/runner/AbstractRunnerTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner;
2 |
3 | import com.github.dreamhead.moco.helper.MocoTestHelper;
4 | import com.github.dreamhead.moco.runner.monitor.FileMonitor;
5 | import org.junit.After;
6 |
7 | import java.io.*;
8 |
9 | import static java.lang.String.format;
10 | import static org.junit.Assert.fail;
11 |
12 | public abstract class AbstractRunnerTest {
13 | protected final MocoTestHelper helper = new MocoTestHelper();
14 | protected Runner runner;
15 |
16 | @After
17 | public void tearDown() {
18 | if (runner != null) {
19 | runner.stop();
20 | }
21 | }
22 |
23 | protected void waitChangeHappens() {
24 | try {
25 | Thread.sleep(FileMonitor.INTERVAL * 2);
26 | } catch (InterruptedException e) {
27 | fail(format("failed to wait change happens: %s", e.getMessage()));
28 | }
29 | }
30 |
31 | protected void changeFileContent(File response, String content) {
32 | PrintStream stream = null;
33 | try {
34 | stream = new PrintStream(new FileOutputStream(response));
35 | stream.print(content);
36 | } catch (IOException e) {
37 | fail("failed to change file content");
38 | } finally {
39 | if (stream != null) {
40 | stream.close();
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/match.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request": {
4 | "uri": {
5 | "match": "/\\w*/foo"
6 | }
7 | },
8 | "response": {
9 | "text": "uri_match"
10 | }
11 | },
12 | {
13 | "request": {
14 | "uri": "/text-match",
15 | "text": {
16 | "match": "\\w*foo"
17 | }
18 | },
19 | "response": {
20 | "text": "text_match"
21 | }
22 | },
23 | {
24 | "request": {
25 | "uri": "/method-match",
26 | "method": {
27 | "match": "get|post"
28 | }
29 | },
30 | "response": {
31 | "text": "method_match"
32 | }
33 | },
34 | {
35 | "request": {
36 | "uri": "/header-match",
37 | "headers": {
38 | "content-type": {
39 | "match" : "application/\\w*"
40 | }
41 | }
42 | },
43 | "response": {
44 | "text": "header_match"
45 | }
46 | },
47 | {
48 | "request": {
49 | "uri": "/query-match",
50 | "queries": {
51 | "foo": {
52 | "match" : "b\\w*"
53 | }
54 | }
55 | },
56 | "response": {
57 | "text": "query_match"
58 | }
59 | }
60 | ]
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/ResponseSetting.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import com.github.dreamhead.moco.handler.AndResponseHandler;
4 | import com.github.dreamhead.moco.resource.Resource;
5 | import io.netty.handler.codec.http.HttpResponseStatus;
6 |
7 | import static com.github.dreamhead.moco.Moco.*;
8 | import static com.google.common.collect.Lists.newArrayList;
9 |
10 | public abstract class ResponseSetting {
11 | protected ResponseHandler handler;
12 |
13 | public void response(String content) {
14 | this.response(text(content));
15 | }
16 |
17 | public void response(Resource resource) {
18 | this.response(with(resource));
19 | }
20 |
21 | public void response(ResponseHandler handler) {
22 | if (handler == null) {
23 | return;
24 | }
25 |
26 | if (this.handler != null) {
27 | throw new RuntimeException("handler has already been set");
28 | }
29 |
30 | this.handler = handler;
31 | }
32 |
33 | public void response(ResponseHandler... handler) {
34 | this.response(new AndResponseHandler(newArrayList(handler)));
35 | }
36 |
37 | public void redirectTo(String url) {
38 | this.response(status(HttpResponseStatus.FOUND.code()), header("Location", url));
39 | }
40 |
41 | protected static RequestMatcher context(String context) {
42 | return match(uri(context + ".*"));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/extractor/HttpMethodExtractorTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.extractor;
2 |
3 | import static org.hamcrest.CoreMatchers.is;
4 | import static org.junit.Assert.assertThat;
5 | import io.netty.handler.codec.http.DefaultFullHttpRequest;
6 | import io.netty.handler.codec.http.FullHttpRequest;
7 | import io.netty.handler.codec.http.HttpMethod;
8 | import io.netty.handler.codec.http.HttpVersion;
9 |
10 | import org.junit.Before;
11 | import org.junit.Test;
12 |
13 | public class HttpMethodExtractorTest {
14 | private HttpMethodExtractor extractor;
15 | private FullHttpRequest request;
16 |
17 | @Before
18 | public void setUp() {
19 | extractor = new HttpMethodExtractor();
20 | }
21 |
22 | @Test
23 | public void should_return_get_with_get_method() {
24 | request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, "/foo");
25 | assertThat(extractor.extract(request), is(HttpMethod.GET.toString()));
26 | }
27 |
28 | @Test
29 | public void should_return_post_with_post_method() {
30 | request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.POST, "/foo");
31 | assertThat(extractor.extract(request), is(HttpMethod.POST.toString()));
32 | }
33 |
34 | @Test
35 | public void should_return_delete_with_delete_method() {
36 | request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.DELETE, "/foo");
37 | assertThat(extractor.extract(request), is(HttpMethod.DELETE.toString()));
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/extractor/FormRequestExtractor.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.extractor;
2 |
3 | import com.github.dreamhead.moco.RequestExtractor;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 | import io.netty.handler.codec.http.multipart.Attribute;
6 | import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
7 | import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
8 | import io.netty.handler.codec.http.multipart.InterfaceHttpData;
9 |
10 | public class FormRequestExtractor implements RequestExtractor {
11 | private final String key;
12 |
13 | public FormRequestExtractor(String key) {
14 | this.key = key;
15 | }
16 |
17 | @Override
18 | public String extract(FullHttpRequest request) {
19 | try {
20 | HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), request);
21 | InterfaceHttpData data = decoder.getBodyHttpData(key);
22 | if (data != null && data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
23 | Attribute attribute = (Attribute)data;
24 | return attribute.getValue();
25 | }
26 |
27 | return null;
28 | } catch (HttpPostRequestDecoder.IncompatibleDataDecoderException idde) {
29 | return null;
30 | } catch (Exception e) {
31 | throw new RuntimeException(e);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/HttpServer.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import com.github.dreamhead.moco.mount.MountHandler;
4 | import com.github.dreamhead.moco.mount.MountMatcher;
5 | import com.github.dreamhead.moco.mount.MountPredicate;
6 | import com.github.dreamhead.moco.mount.MountTo;
7 | import io.netty.handler.codec.http.HttpMethod;
8 |
9 | import java.io.File;
10 |
11 | import static com.github.dreamhead.moco.Moco.*;
12 | import static com.google.common.collect.ImmutableList.copyOf;
13 |
14 | public abstract class HttpServer extends ResponseSetting {
15 | protected abstract Setting onRequestAttached(RequestMatcher matcher);
16 |
17 | public Setting request(RequestMatcher matcher) {
18 | return this.onRequestAttached(matcher);
19 | }
20 |
21 | public Setting request(RequestMatcher... matchers) {
22 | return request(or(matchers));
23 | }
24 |
25 | public Setting get(RequestMatcher matcher) {
26 | return request(and(by(method(HttpMethod.GET.name())), matcher));
27 | }
28 |
29 | public Setting post(RequestMatcher matcher) {
30 | return request(and(by(method(HttpMethod.POST.name())), matcher));
31 | }
32 |
33 | public void mount(final String dir, final MountTo target, final MountPredicate... predicates) {
34 | File mountedDir = new File(dir);
35 | this.request(new MountMatcher(mountedDir, target, copyOf(predicates))).response(new MountHandler(mountedDir, target));
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/handler/AndResponseHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.handler;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 | import com.github.dreamhead.moco.ResponseHandler;
5 | import com.google.common.base.Function;
6 | import io.netty.handler.codec.http.FullHttpRequest;
7 | import io.netty.handler.codec.http.FullHttpResponse;
8 |
9 | import static com.google.common.collect.FluentIterable.from;
10 |
11 | public class AndResponseHandler implements ResponseHandler {
12 | private final Iterable handlers;
13 |
14 | public AndResponseHandler(Iterable handlers) {
15 | this.handlers = handlers;
16 | }
17 |
18 | @Override
19 | public void writeToResponse(FullHttpRequest request, FullHttpResponse response) {
20 | for (ResponseHandler handler : handlers) {
21 | handler.writeToResponse(request, response);
22 | }
23 | }
24 |
25 | @Override
26 | public ResponseHandler apply(final MocoConfig config) {
27 | return new AndResponseHandler(from(handlers).transform(applyConfig(config)));
28 | }
29 |
30 | private Function applyConfig(final MocoConfig config) {
31 | return new Function() {
32 | @Override
33 | public ResponseHandler apply(ResponseHandler handler) {
34 | return handler.apply(config);
35 | }
36 | };
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/StartTask.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.bootstrap;
2 |
3 | import com.github.dreamhead.moco.runner.*;
4 |
5 | import static com.github.dreamhead.moco.bootstrap.StartArgs.parse;
6 |
7 | public class StartTask implements BootstrapTask {
8 | private final int defaultShutdownPort;
9 | private final RunnerFactory factory;
10 |
11 | public StartTask(int defaultShutdownPort, String defaultShutdownKey) {
12 | this.factory = new RunnerFactory(defaultShutdownPort, defaultShutdownKey);
13 | this.defaultShutdownPort = defaultShutdownPort;
14 | }
15 |
16 | @Override
17 | public void run(String[] args) {
18 | StartArgs startArgs = parse(args);
19 | if (conflictWithDefaultShutdownPort(startArgs, defaultShutdownPort)) {
20 | System.err.println("port is same as default shutdown port, please specify another port or shutdown port.");
21 | return;
22 | }
23 |
24 | final Runner runner = factory.createRunner(startArgs);
25 | runner.run();
26 |
27 | Runtime.getRuntime().addShutdownHook(new Thread() {
28 | @Override
29 | public void run() {
30 | runner.stop();
31 | }
32 | });
33 | }
34 |
35 | private boolean conflictWithDefaultShutdownPort(StartArgs startArgs, int defaultShutdownPort) {
36 | return startArgs.getPort() == defaultShutdownPort && !startArgs.hasShutdonwPort();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/util/FileContentType.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.util;
2 |
3 | import com.google.common.collect.ImmutableMap;
4 | import com.google.common.io.Files;
5 |
6 | public class FileContentType {
7 | public static final String DEFAULT_CONTENT_TYPE = "text/plain; charset=UTF-8";
8 |
9 | private static final ImmutableMap contentTypeMap = ImmutableMap.builder()
10 | .put("png", "image/png")
11 | .put("gif", "image/gif")
12 | .put("jpg", "image/jpeg")
13 | .put("jpeg", "image/jpeg")
14 | .put("tiff", "image/tiff")
15 | .put("css", "text/css")
16 | .put("html", "text/html")
17 | .put("txt", "text/plain")
18 | .put("js", "application/x-javascript")
19 | .put("json", "application/json")
20 | .put("pdf", "application/pdf")
21 | .put("zip", "application/zip")
22 | .put("xml", "text/xml")
23 | .build();
24 |
25 | private final String filename;
26 |
27 | public FileContentType(String filename) {
28 | this.filename = filename;
29 | }
30 |
31 | public String getContentType() {
32 | return toContentType(Files.getFileExtension(filename));
33 | }
34 |
35 | private String toContentType(String extension) {
36 | String contentType = contentTypeMap.get(extension.toLowerCase());
37 | return contentType != null ? contentType : DEFAULT_CONTENT_TYPE;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/MocoXmlStandaloneTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import org.junit.Test;
4 |
5 | import java.io.IOException;
6 |
7 | import static com.github.dreamhead.moco.RemoteTestUtils.remoteUrl;
8 | import static org.hamcrest.CoreMatchers.is;
9 | import static org.junit.Assert.assertThat;
10 |
11 | public class MocoXmlStandaloneTest extends AbstractMocoStandaloneTest {
12 | @Test
13 | public void should_return_expected_response_based_on_specified_xpath_request() throws IOException {
14 | runWithConfiguration("xpath.json");
15 | assertThat(helper.postFile(remoteUrl("/xpath"), "foo.xml"), is("response_for_xpath_request"));
16 | }
17 |
18 | @Test(expected = IOException.class)
19 | public void should_throw_exception_for_unknown_xpath_request() throws IOException {
20 | runWithConfiguration("xpath.json");
21 | helper.postFile(remoteUrl("/xpath"), "bar.xml");
22 | }
23 |
24 | @Test
25 | public void should_return_expected_response_based_on_specified_xml_request() throws IOException {
26 | runWithConfiguration("xml.json");
27 | assertThat(helper.postFile(remoteUrl("/xml"), "foo.xml"), is("response_for_xml_request"));
28 | }
29 |
30 | @Test
31 | public void should_return_expected_response_based_on_specified_xml_file_request() throws IOException {
32 | runWithConfiguration("xml.json");
33 | assertThat(helper.postFile(remoteUrl("/xmlfile"), "foo.xml"), is("response_for_xml_file_request"));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/handler/ResponseHandlers.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.handler;
2 |
3 | import com.github.dreamhead.moco.ResponseHandler;
4 | import com.github.dreamhead.moco.resource.Resource;
5 | import com.google.common.collect.ImmutableMap;
6 |
7 | import java.lang.reflect.Constructor;
8 |
9 | import static java.lang.String.format;
10 |
11 | public class ResponseHandlers {
12 | private static final ImmutableMap handlers = ImmutableMap.builder()
13 | .put("file", ContentHandler.class)
14 | .put("text", ContentHandler.class)
15 | .put("pathresource", ContentHandler.class)
16 | .put("template", ContentHandler.class)
17 | .put("version", VersionResponseHandler.class).build();
18 |
19 | public static ResponseHandler responseHandler(Resource resource) {
20 | if (handlers.containsKey(resource.id())) {
21 | return createResponseHandler(resource);
22 | }
23 |
24 | throw new IllegalArgumentException(format("unknown response handler for [%s]", resource.id()));
25 | }
26 |
27 | private static ResponseHandler createResponseHandler(Resource resource) {
28 | Class clazz = handlers.get(resource.id());
29 | try {
30 | Constructor[] constructors = clazz.getConstructors();
31 | return (ResponseHandler)constructors[0].newInstance(resource);
32 | } catch (Exception e) {
33 | throw new RuntimeException(e);
34 | }
35 | }
36 |
37 | private ResponseHandlers() {
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/setting/BaseSetting.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.setting;
2 |
3 | import com.github.dreamhead.moco.ConfigApplier;
4 | import com.github.dreamhead.moco.MocoConfig;
5 | import com.github.dreamhead.moco.RequestMatcher;
6 | import com.github.dreamhead.moco.Setting;
7 | import com.github.dreamhead.moco.matcher.AndRequestMatcher;
8 | import io.netty.handler.codec.http.FullHttpRequest;
9 | import io.netty.handler.codec.http.FullHttpResponse;
10 |
11 | import static com.github.dreamhead.moco.util.Configs.configItem;
12 | import static com.google.common.collect.ImmutableList.of;
13 |
14 | public class BaseSetting extends Setting implements ConfigApplier {
15 | public BaseSetting(RequestMatcher matcher) {
16 | super(matcher);
17 | }
18 |
19 | public boolean match(FullHttpRequest request) {
20 | return this.matcher.match(request) && this.handler != null;
21 | }
22 |
23 | public void writeToResponse(FullHttpRequest request, FullHttpResponse response) {
24 | this.handler.writeToResponse(request, response);
25 | }
26 |
27 | public BaseSetting apply(final MocoConfig config) {
28 | RequestMatcher appliedMatcher = configItem(this.matcher, config);
29 | if (config.isFor("uri") && this.matcher == appliedMatcher) {
30 | appliedMatcher = new AndRequestMatcher(of(appliedMatcher, context(config.apply(""))));
31 | }
32 |
33 | BaseSetting setting = new BaseSetting(appliedMatcher);
34 | setting.handler = configItem(this.handler, config);
35 | return setting;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/runner/DynamicSettingRunnerTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner;
2 |
3 | import com.github.dreamhead.moco.bootstrap.StartArgs;
4 | import org.junit.Test;
5 |
6 | import java.io.File;
7 | import java.io.IOException;
8 |
9 | import static com.github.dreamhead.moco.RemoteTestUtils.port;
10 | import static com.github.dreamhead.moco.RemoteTestUtils.root;
11 | import static org.hamcrest.CoreMatchers.is;
12 | import static org.junit.Assert.assertThat;
13 |
14 | public class DynamicSettingRunnerTest extends AbstractRunnerTest {
15 | @Test
16 | public void should_reload_configuration() throws IOException, InterruptedException {
17 | final File config = File.createTempFile("config", ".json");
18 | changeFileContent(config, "[{\"response\" :{" +
19 | "\"text\" : \"foo\"" +
20 | "}}]");
21 |
22 | final File setting = File.createTempFile("setting", ".json");
23 | changeFileContent(setting, "[{" +
24 | "\"include\" : \"" + config.getAbsolutePath() + "\"" +
25 | "}]");
26 |
27 | RunnerFactory factory = new RunnerFactory(9090, "SHUTDOWN");
28 | runner = factory.createRunner(new StartArgs(port(), 9090, null, setting.getAbsolutePath(), null));
29 | runner.run();
30 | assertThat(helper.get(root()), is("foo"));
31 |
32 | changeFileContent(config, "[{\"response\" :{" +
33 | "\"text\" : \"foobar\"" +
34 | "}}]");
35 |
36 | waitChangeHappens();
37 |
38 | assertThat(helper.get(root()), is("foobar"));
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/model/DefaultRequest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonAutoDetect;
4 | import com.google.common.base.Objects;
5 |
6 | import java.util.Map;
7 |
8 | import static com.google.common.collect.Maps.newHashMap;
9 |
10 | @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
11 | public class DefaultRequest extends Message implements Request {
12 | private Map queries = newHashMap();
13 | private String method;
14 |
15 | public void addQuery(String key, String value) {
16 | this.queries.put(key, value);
17 | }
18 |
19 | public Map getQueries() {
20 | return queries;
21 | }
22 |
23 | public void setMethod(String method) {
24 | this.method = method;
25 | }
26 |
27 | public String getMethod() {
28 | return method;
29 | }
30 |
31 | public boolean match(DefaultRequest that) {
32 | return super.match(that) && doMatch(method, that.method)
33 | && doMatch(queries, that.queries);
34 | }
35 |
36 | @Override
37 | public int hashCode() {
38 | return Objects.hashCode(super.hashCode(), queries, method);
39 | }
40 |
41 | @Override
42 | public String toString() {
43 | return Objects.toStringHelper(DefaultRequest.class)
44 | .omitNullValues()
45 | .add("version", version)
46 | .add("queries", queries)
47 | .add("method", method)
48 | .add("headers", headers)
49 | .add("content", content)
50 | .toString();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/Bootstrap.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.bootstrap;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | public class Bootstrap implements BootstrapTask {
7 | private static Logger logger = LoggerFactory.getLogger(Bootstrap.class);
8 | private static final int DEFAULT_SHUTDOWN_PORT = 9527;
9 | private static final String DEFAULT_SHUTDOWN_KEY = "_SHUTDOWN_MOCO_KEY";
10 |
11 | private final BootstrapTask startTask = new StartTask(DEFAULT_SHUTDOWN_PORT, DEFAULT_SHUTDOWN_KEY);
12 | private final BootstrapTask shutdownTask = new ShutdownTask(DEFAULT_SHUTDOWN_PORT, DEFAULT_SHUTDOWN_KEY);
13 |
14 | @Override
15 | public void run(String[] args) {
16 | try {
17 | if (args.length < 1) {
18 | throw new ParseArgException("task name needs to be specified");
19 | }
20 |
21 | if ("start".equals(args[0])) {
22 | startTask.run(args);
23 | return;
24 | }
25 |
26 | if ("shutdown".equals(args[0])) {
27 | shutdownTask.run(args);
28 | return;
29 | }
30 |
31 | help();
32 | } catch (ParseArgException e) {
33 | help();
34 | } catch (Exception e) {
35 | logger.error(e.getMessage());
36 | }
37 | }
38 |
39 | private void help() {
40 | System.out.println("moco start -p port -c [configuration file] {-s [shutdown port]}");
41 | System.out.println("or");
42 | System.out.println("moco start -p port -g [global settings file] {-e [environment]} {-s [shutdown port]}");
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/MocoMountStandaloneTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import org.apache.http.client.HttpResponseException;
4 | import org.junit.Test;
5 |
6 | import java.io.IOException;
7 |
8 | import static com.github.dreamhead.moco.RemoteTestUtils.remoteUrl;
9 | import static org.hamcrest.CoreMatchers.is;
10 | import static org.junit.Assert.assertThat;
11 |
12 | public class MocoMountStandaloneTest extends AbstractMocoStandaloneTest {
13 | @Test
14 | public void should_mount_dir_to_uri() throws IOException {
15 | runWithConfiguration("mount.json");
16 | assertThat(helper.get(remoteUrl("/mount/mount.response")), is("response from mount"));
17 | }
18 |
19 | @Test
20 | public void should_mount_dir_to_uri_with_include() throws IOException {
21 | runWithConfiguration("mount.json");
22 | assertThat(helper.get(remoteUrl("/mount-include/mount.response")), is("response from mount"));
23 | }
24 |
25 | @Test(expected = HttpResponseException.class)
26 | public void should_return_non_inclusion() throws IOException {
27 | runWithConfiguration("mount.json");
28 | helper.get(remoteUrl("/mount-include/foo.bar"));
29 | }
30 |
31 | @Test
32 | public void should_mount_dir_to_uri_with_exclude() throws IOException {
33 | runWithConfiguration("mount.json");
34 | assertThat(helper.get(remoteUrl("/mount-exclude/foo.bar")), is("foo.bar"));
35 | }
36 |
37 | @Test(expected = HttpResponseException.class)
38 | public void should_return_exclusion() throws IOException {
39 | runWithConfiguration("mount.json");
40 | helper.get(remoteUrl("/mount-exclude/mount.response"));
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/extractor/ParamRequestExtractorTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.extractor;
2 |
3 | import io.netty.handler.codec.http.FullHttpRequest;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 |
7 | import static org.hamcrest.CoreMatchers.is;
8 | import static org.hamcrest.CoreMatchers.nullValue;
9 | import static org.junit.Assert.assertThat;
10 | import static org.mockito.Mockito.mock;
11 | import static org.mockito.Mockito.when;
12 |
13 | public class ParamRequestExtractorTest {
14 | private ParamRequestExtractor extractor;
15 | private FullHttpRequest request;
16 |
17 | @Before
18 | public void setUp() throws Exception {
19 | extractor = new ParamRequestExtractor("param");
20 | request = mock(FullHttpRequest.class);
21 | }
22 |
23 | @Test
24 | public void should_return_null_if_uri_has_no_parameter() {
25 | when(request.getUri()).thenReturn("/foo");
26 | assertThat(extractor.extract(request), nullValue());
27 | }
28 |
29 | @Test
30 | public void should_return_parameter_value_if_uri_has_same_parameter() {
31 | when(request.getUri()).thenReturn("/foo?param=bar");
32 | assertThat(extractor.extract(request), is("bar"));
33 | }
34 |
35 | @Test
36 | public void should_extract_parameter_value_from_multiple_parameters() {
37 | when(request.getUri()).thenReturn("/foo?param=bar¶m2=bar2");
38 | assertThat(extractor.extract(request), is("bar"));
39 | }
40 |
41 | @Test
42 | public void should_return_null_if_uri_has_no_same_parameter() {
43 | when(request.getUri()).thenReturn("/foo?param2=bar");
44 | assertThat(extractor.extract(request), nullValue());
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/extractor/XPathRequestExtractor.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.extractor;
2 |
3 | import com.github.dreamhead.moco.RequestExtractor;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 | import org.w3c.dom.Node;
6 | import org.w3c.dom.NodeList;
7 |
8 | import javax.xml.xpath.*;
9 | import java.util.List;
10 |
11 | import static com.google.common.collect.Lists.newArrayList;
12 |
13 | public class XPathRequestExtractor implements RequestExtractor {
14 | private final XmlExtractorHelper helper = new XmlExtractorHelper();
15 | private final ContentRequestExtractor extractor = new ContentRequestExtractor();
16 | private final XPathExpression xPathExpression;
17 |
18 | public XPathRequestExtractor(String xpath) {
19 | XPathFactory xPathfactory = XPathFactory.newInstance();
20 | XPath target = xPathfactory.newXPath();
21 | try {
22 | xPathExpression = target.compile(xpath);
23 | } catch (XPathExpressionException e) {
24 | throw new IllegalArgumentException(e);
25 | }
26 | }
27 |
28 | @Override
29 | public String[] extract(FullHttpRequest request) {
30 | try {
31 | NodeList list = (NodeList) xPathExpression.evaluate(helper.extractAsInputSource(request, extractor), XPathConstants.NODESET);
32 | List values = newArrayList();
33 | for (int i = 0; i < list.getLength(); i++) {
34 | Node node = list.item(i);
35 | values.add(node.getNodeValue());
36 | }
37 | return values.toArray(new String[values.size()]);
38 | } catch (XPathExpressionException e) {
39 | return new String[0];
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/internal/MocoServer.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.internal;
2 |
3 | import io.netty.bootstrap.ServerBootstrap;
4 | import io.netty.channel.*;
5 | import io.netty.channel.nio.NioEventLoopGroup;
6 | import io.netty.channel.socket.nio.NioServerSocketChannel;
7 |
8 | public class MocoServer {
9 | private EventLoopGroup bossGroup;
10 | private EventLoopGroup workerGroup;
11 | private ChannelFuture future;
12 |
13 | public MocoServer() {
14 | bossGroup = new NioEventLoopGroup();
15 | workerGroup = new NioEventLoopGroup();
16 | }
17 |
18 | public void start(final int port, ChannelHandler pipelineFactory) {
19 | ServerBootstrap bootstrap = new ServerBootstrap();
20 | bootstrap.group(bossGroup, workerGroup)
21 | .channel(NioServerSocketChannel.class)
22 | .childHandler(pipelineFactory)
23 | .option(ChannelOption.TCP_NODELAY, true)
24 | .childOption(ChannelOption.SO_KEEPALIVE, true);
25 |
26 | try {
27 | future = bootstrap.bind(port).sync();
28 | } catch (InterruptedException e) {
29 | throw new RuntimeException(e);
30 | }
31 | }
32 |
33 | public void stop() {
34 | doStop();
35 | }
36 |
37 | private void doStop() {
38 | if (future != null) {
39 | future.channel().close().syncUninterruptibly();
40 | future = null;
41 | }
42 |
43 | if (bossGroup != null) {
44 | bossGroup.shutdownGracefully();
45 | bossGroup = null;
46 | }
47 |
48 |
49 | if (workerGroup != null) {
50 | workerGroup.shutdownGracefully();
51 | workerGroup = null;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/mount/MountMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.mount;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 | import com.github.dreamhead.moco.RequestMatcher;
5 | import io.netty.handler.codec.http.FullHttpRequest;
6 |
7 | import java.io.File;
8 |
9 | import static com.google.common.base.Predicates.and;
10 | import static com.google.common.base.Strings.isNullOrEmpty;
11 |
12 | public class MountMatcher implements RequestMatcher {
13 | private final MountPathExtractor extractor;
14 |
15 | private final File dir;
16 | private final Iterable predicates;
17 | private final MountTo target;
18 |
19 | public MountMatcher(File dir, MountTo target, Iterable predicates) {
20 | this.dir = dir;
21 | this.predicates = predicates;
22 | this.target = target;
23 | this.extractor = new MountPathExtractor(target);
24 | }
25 |
26 | @Override
27 | public boolean match(FullHttpRequest request) {
28 | String relativePath = extractor.extract(request);
29 | return isTarget(relativePath) && new File(dir, relativePath).exists();
30 | }
31 |
32 | @Override
33 | public RequestMatcher apply(final MocoConfig config) {
34 | if (config.isFor("uri")) {
35 | return new MountMatcher(this.dir, this.target.apply(config), this.predicates);
36 | }
37 |
38 | if (config.isFor("file")) {
39 | return new MountMatcher(new File(config.apply(this.dir.getName())), this.target, this.predicates);
40 | }
41 |
42 | return this;
43 | }
44 |
45 | private boolean isTarget(String relativePath) {
46 | return !isNullOrEmpty(relativePath) && and(predicates).apply(relativePath);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/MocoWebTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import org.apache.http.client.fluent.Request;
4 | import org.junit.Test;
5 |
6 | import java.io.IOException;
7 |
8 | import static com.github.dreamhead.moco.Moco.*;
9 | import static com.github.dreamhead.moco.RemoteTestUtils.remoteUrl;
10 | import static com.github.dreamhead.moco.RemoteTestUtils.root;
11 | import static com.github.dreamhead.moco.Runner.running;
12 | import static org.hamcrest.CoreMatchers.is;
13 | import static org.junit.Assert.assertThat;
14 |
15 | public class MocoWebTest extends AbstractMocoTest {
16 | @Test
17 | public void should_set_and_recognize_cookie() throws Exception {
18 | server.request(eq(cookie("loggedIn"), "true")).response(status(200));
19 | server.response(cookie("loggedIn", "true"), status(302));
20 |
21 | running(server, new Runnable() {
22 | @Override
23 | public void run() throws IOException {
24 | int statusBeforeLogin = Request.Get(root()).execute().returnResponse().getStatusLine().getStatusCode();
25 | assertThat(statusBeforeLogin, is(302));
26 | int statusAfterLogin = Request.Get(root()).execute().returnResponse().getStatusLine().getStatusCode();
27 | assertThat(statusAfterLogin, is(200));
28 | }
29 | });
30 | }
31 |
32 | @Test
33 | public void should_redirect_to_expected_url() throws Exception {
34 | server.get(by(uri("/"))).response("foo");
35 | server.get(by(uri("/redirectTo"))).redirectTo(root());
36 |
37 | running(server, new Runnable() {
38 | @Override
39 | public void run() throws IOException {
40 | assertThat(helper.get(remoteUrl("/redirectTo")), is("foo"));
41 | }
42 | });
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/bootstrap/StartArgsTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.bootstrap;
2 |
3 | import org.junit.Test;
4 |
5 | import static com.github.dreamhead.moco.bootstrap.StartArgs.parse;
6 | import static org.hamcrest.CoreMatchers.is;
7 | import static org.junit.Assert.assertThat;
8 |
9 | public class StartArgsTest {
10 | @Test
11 | public void should_parse_start_arguments() {
12 | StartArgs args = parse("start", "-p", "12306", "-c", "foo.json");
13 | assertThat(args.getPort(), is(12306));
14 | assertThat(args.getConfigurationFile().get(), is("foo.json"));
15 | }
16 |
17 | @Test
18 | public void should_parse_settings() {
19 | StartArgs args = parse("start", "-p", "12306", "-g", "settings.json");
20 | assertThat(args.getSettings().get(), is("settings.json"));
21 | }
22 |
23 | @Test
24 | public void should_parse_environment() {
25 | StartArgs args = parse("start", "-p", "12306", "-g", "setting.json", "-e", "foo");
26 | assertThat(args.getEnv().get(), is("foo"));
27 | }
28 |
29 | @Test(expected = ParseArgException.class)
30 | public void should_set_at_least_config_or_settings() {
31 | parse("start", "-p", "12306");
32 | }
33 |
34 | @Test(expected = ParseArgException.class)
35 | public void should_not_set_config_and_settings() {
36 | parse("start", "-p", "12306", "-c", "foo.json", "-g", "settings.json");
37 | }
38 |
39 | @Test(expected = ParseArgException.class)
40 | public void should_not_set_environment_without_global_settings() {
41 | parse("start", "-p", "12306", "-e", "foo");
42 | }
43 |
44 | @Test(expected = ParseArgException.class)
45 | public void should_not_set_environment_with_config() {
46 | parse("start", "-p", "12306", "-c", "foo.json", "-e", "foo");
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/matcher/JsonRequestMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.matcher;
2 |
3 | import com.fasterxml.jackson.core.JsonProcessingException;
4 | import com.fasterxml.jackson.databind.JsonNode;
5 | import com.fasterxml.jackson.databind.ObjectMapper;
6 | import com.github.dreamhead.moco.MocoConfig;
7 | import com.github.dreamhead.moco.RequestExtractor;
8 | import com.github.dreamhead.moco.RequestMatcher;
9 | import com.github.dreamhead.moco.resource.Resource;
10 | import io.netty.handler.codec.http.FullHttpRequest;
11 |
12 | import java.io.IOException;
13 |
14 | public class JsonRequestMatcher implements RequestMatcher {
15 | private final RequestExtractor extractor;
16 | private final Resource resource;
17 | private final ObjectMapper mapper;
18 |
19 | public JsonRequestMatcher(RequestExtractor extractor, Resource resource) {
20 | this.extractor = extractor;
21 | this.resource = resource;
22 | this.mapper = new ObjectMapper();
23 | }
24 |
25 | @Override
26 | public boolean match(FullHttpRequest request) {
27 | try {
28 | JsonNode requestNode = mapper.readTree(extractor.extract(request));
29 | JsonNode resourceNode = mapper.readTree(resource.readFor(request));
30 | return requestNode.equals(resourceNode);
31 | } catch (JsonProcessingException jpe) {
32 | return false;
33 | } catch (IOException e) {
34 | throw new RuntimeException(e);
35 | }
36 | }
37 |
38 | @Override
39 | public RequestMatcher apply(final MocoConfig config) {
40 | Resource appliedResource = this.resource.apply(config);
41 | if (appliedResource == this.resource) {
42 | return this;
43 | }
44 |
45 | return new JsonRequestMatcher(this.extractor, appliedResource);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/MocoJsonTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import org.junit.Test;
4 |
5 | import java.io.IOException;
6 |
7 | import static com.github.dreamhead.moco.Moco.*;
8 | import static com.github.dreamhead.moco.RemoteTestUtils.root;
9 | import static com.github.dreamhead.moco.Runner.running;
10 | import static org.hamcrest.CoreMatchers.is;
11 | import static org.junit.Assert.assertThat;
12 |
13 | public class MocoJsonTest extends AbstractMocoTest {
14 | @Test
15 | public void should_return_content_based_on_jsonpath() throws Exception {
16 | server.request(eq(jsonPath("$.book[*].price"), "1")).response("jsonpath match success");
17 | running(server, new Runnable() {
18 | @Override
19 | public void run() throws IOException {
20 | assertThat(helper.postContent(root(), "{\"book\":{\"price\":\"1\"}}"),
21 | is("jsonpath match success"));
22 | }
23 | });
24 | }
25 |
26 | @Test
27 | public void should_match_extact_json() throws Exception {
28 | final String jsonContent = "{\"foo\":\"bar\"}";
29 | server.request(json(text(jsonContent))).response("foo");
30 | running(server, new Runnable() {
31 | @Override
32 | public void run() throws IOException {
33 | assertThat(helper.postContent(root(), jsonContent), is("foo"));
34 | }
35 | });
36 | }
37 |
38 | @Test
39 | public void should_match_same_structure_json() throws Exception {
40 | server.request(json(text("{\"foo\":\"bar\"}"))).response("foo");
41 | running(server, new Runnable() {
42 | @Override
43 | public void run() throws IOException {
44 | assertThat(helper.postContent(root(), "{\n\t\"foo\":\"bar\"\n}"), is("foo"));
45 | }
46 | });
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/moco-runner/src/test/resources/template.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request": {
4 | "uri": "/template"
5 | },
6 | "response": {
7 | "text": {
8 | "template": "${req.method}"
9 | }
10 | }
11 | },
12 | {
13 | "request": {
14 | "uri": "/file_template"
15 | },
16 | "response": {
17 | "file": {
18 | "template": "src/test/resources/template.foo"
19 | }
20 | }
21 | },
22 | {
23 | "request": {
24 | "uri": "/path_resource_template"
25 | },
26 | "response": {
27 | "path_resource": {
28 | "template": "template.foo"
29 | }
30 | }
31 | },
32 | {
33 | "request": {
34 | "uri": "/version_template"
35 | },
36 | "response": {
37 | "version": {
38 | "template": "${req.version}"
39 | }
40 | }
41 | },
42 | {
43 | "request": {
44 | "uri": "/header_template"
45 | },
46 | "response": {
47 | "headers": {
48 | "foo": {
49 | "template" : "${req.headers['foo']}"
50 | }
51 | }
52 | }
53 | },
54 | {
55 | "request": {
56 | "uri": "/cookie_template",
57 | "cookies": {
58 | "foo" : "GET"
59 | }
60 | },
61 | "response": {
62 | "text" : "OK"
63 | }
64 | },
65 | {
66 | "request": {
67 | "uri": "/cookie_template"
68 | },
69 | "response": {
70 | "cookies": {
71 | "foo": {
72 | "template" : "${req.method}"
73 | }
74 | },
75 | "status" : 302
76 | }
77 | }
78 | ]
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/parser/model/RequestSetting.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.parser.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonAutoDetect;
4 | import com.fasterxml.jackson.annotation.JsonProperty;
5 | import com.github.dreamhead.moco.RequestMatcher;
6 | import com.github.dreamhead.moco.parser.RequestMatcherFactory;
7 | import com.google.common.base.Objects;
8 |
9 | import java.util.Map;
10 |
11 | @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
12 | public class RequestSetting {
13 | private final RequestMatcherFactory factory = new DynamicRequestMatcherFactory();
14 |
15 | private TextContainer text;
16 | private TextContainer uri;
17 | private TextContainer file;
18 | @JsonProperty("path_resource")
19 | private TextContainer pathResource;
20 | private TextContainer method;
21 | private TextContainer version;
22 |
23 | private Map headers;
24 | private Map xpaths;
25 | @JsonProperty("json_paths")
26 | private Map jsonPaths;
27 | private Map queries;
28 | private Map cookies;
29 | private Map forms;
30 |
31 | @Override
32 | public String toString() {
33 | return Objects.toStringHelper(this).omitNullValues()
34 | .add("version", version)
35 | .add("URI", uri)
36 | .add("text", text)
37 | .add("file", file)
38 | .add("headers", headers)
39 | .add("xpaths", xpaths)
40 | .add("queries", queries)
41 | .add("cookies", cookies)
42 | .add("forms", forms)
43 | .toString();
44 | }
45 |
46 | public RequestMatcher getRequestMatcher() {
47 | return factory.createRequestMatcher(this);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/handler/SequenceContentHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.handler;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 | import com.github.dreamhead.moco.ResponseHandler;
5 | import com.github.dreamhead.moco.resource.Resource;
6 | import com.google.common.base.Function;
7 | import com.google.common.collect.FluentIterable;
8 | import io.netty.buffer.ByteBuf;
9 | import io.netty.handler.codec.http.FullHttpRequest;
10 |
11 | import static com.google.common.collect.FluentIterable.from;
12 | import static com.google.common.collect.Lists.newArrayList;
13 |
14 | public class SequenceContentHandler extends AbstractContentResponseHandler {
15 | private final Resource[] resources;
16 | private int index;
17 |
18 | public SequenceContentHandler(final Resource[] resources) {
19 | this.resources = resources;
20 | }
21 |
22 | @Override
23 | protected void writeContentResponse(FullHttpRequest request, ByteBuf buffer) {
24 | buffer.writeBytes(resources[current()].readFor(request));
25 | }
26 |
27 | private int current() {
28 | int current = this.index;
29 | if (++index >= resources.length) {
30 | index = resources.length - 1;
31 | }
32 |
33 | return current;
34 | }
35 |
36 | @Override
37 | public ResponseHandler apply(final MocoConfig config) {
38 | FluentIterable transformedResources = from(newArrayList(resources)).transform(applyConfig(config));
39 | return new SequenceContentHandler(transformedResources.toArray(Resource.class));
40 | }
41 |
42 | private Function applyConfig(final MocoConfig config) {
43 | return new Function() {
44 | @Override
45 | public Resource apply(Resource handler) {
46 | return handler.apply(config);
47 | }
48 | };
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/parser/model/MountSetting.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.parser.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonAutoDetect;
4 | import com.github.dreamhead.moco.mount.MountPredicate;
5 | import com.google.common.base.Function;
6 |
7 | import java.util.List;
8 |
9 | import static com.github.dreamhead.moco.MocoMount.exclude;
10 | import static com.github.dreamhead.moco.MocoMount.include;
11 | import static com.google.common.collect.ImmutableList.of;
12 | import static com.google.common.collect.Iterables.*;
13 |
14 | @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
15 | public class MountSetting {
16 | private String dir;
17 | private String uri;
18 | private List includes = of();
19 | private List excludes = of();
20 |
21 | public String getDir() {
22 | return dir;
23 | }
24 |
25 | public String getUri() {
26 | return uri;
27 | }
28 |
29 | public MountPredicate[] getMountPredicates() {
30 | return toArray(toMountPredicates(), MountPredicate.class);
31 | }
32 |
33 | private Iterable toMountPredicates() {
34 | return unmodifiableIterable(concat(
35 | transform(includes, toInclude()),
36 | transform(excludes, toExclude())));
37 | }
38 |
39 | private Function toInclude() {
40 | return new Function() {
41 | @Override
42 | public MountPredicate apply(String input) {
43 | return include(input);
44 | }
45 | };
46 | }
47 |
48 | private Function toExclude() {
49 | return new Function() {
50 | @Override
51 | public MountPredicate apply(String input) {
52 | return exclude(input);
53 | }
54 | };
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/parser/model/ProxyContainerDeserializer.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.parser.model;
2 |
3 | import com.fasterxml.jackson.core.JsonParser;
4 | import com.fasterxml.jackson.core.JsonProcessingException;
5 | import com.fasterxml.jackson.core.JsonToken;
6 | import com.fasterxml.jackson.databind.DeserializationContext;
7 | import com.fasterxml.jackson.databind.JsonDeserializer;
8 |
9 | import java.io.IOException;
10 | import java.util.Map;
11 |
12 | import static com.google.common.collect.Maps.newHashMap;
13 |
14 | public class ProxyContainerDeserializer extends JsonDeserializer {
15 | @Override
16 | public ProxyContainer deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
17 | JsonToken currentToken = jp.getCurrentToken();
18 | if (currentToken == JsonToken.VALUE_STRING) {
19 | return new ProxyContainer(jp.getText().trim(), null);
20 | } else if (currentToken == JsonToken.START_OBJECT) {
21 | JsonToken jsonToken = jp.nextToken();
22 | if (jsonToken == JsonToken.FIELD_NAME) {
23 | return createFailoverProxy(jp);
24 | }
25 | }
26 |
27 | throw ctxt.mappingException(TextContainer.class, currentToken);
28 | }
29 |
30 | private ProxyContainer createFailoverProxy(JsonParser jp) throws IOException {
31 | Map fields = newHashMap();
32 | fetchField(fields, jp);
33 | fetchField(fields, jp);
34 | return new ProxyContainer(fields.get("url"), fields.get("failover"));
35 | }
36 |
37 | private void fetchField(Map fields, JsonParser jp) throws IOException {
38 | String fieldName = jp.getText().trim();
39 | jp.nextToken();
40 | String fieldValue = jp.getText().trim();
41 | jp.nextToken();
42 | fields.put(fieldName.toLowerCase(), fieldValue);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/model/MessageFactory.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.model;
2 |
3 | import com.google.common.base.Strings;
4 | import io.netty.handler.codec.http.*;
5 |
6 | import java.nio.charset.Charset;
7 | import java.util.List;
8 | import java.util.Map;
9 |
10 | public class MessageFactory {
11 | private static void setContent(FullHttpMessage message, Message dumpedMessage) {
12 | String text = message.content().toString(Charset.defaultCharset());
13 | if (!Strings.isNullOrEmpty(text)) {
14 | dumpedMessage.setContent(text);
15 | }
16 | }
17 |
18 | public static DefaultRequest createRequest(FullHttpRequest request) {
19 | DefaultRequest dumpedRequest = new DefaultRequest();
20 | dumpedRequest.setVersion(request.getProtocolVersion().text());
21 | setContent(request, dumpedRequest);
22 | dumpedRequest.setMethod(request.getMethod().name());
23 |
24 | QueryStringDecoder decoder = new QueryStringDecoder(request.getUri());
25 | for (Map.Entry> entry : decoder.parameters().entrySet()) {
26 | dumpedRequest.addQuery(entry.getKey(), entry.getValue().get(0));
27 | }
28 |
29 | for (Map.Entry entry : request.headers()) {
30 | dumpedRequest.addHeader(entry.getKey(), entry.getValue());
31 | }
32 |
33 | return dumpedRequest;
34 | }
35 |
36 | public static Response createResponse(FullHttpResponse response) {
37 | Response dumpedResponse = new Response();
38 | dumpedResponse.setStatusCode(response.getStatus().code());
39 | dumpedResponse.setVersion(response.getProtocolVersion().text());
40 | for (Map.Entry entry : response.headers()) {
41 | dumpedResponse.addHeader(entry.getKey(), entry.getValue());
42 | }
43 | setContent(response, dumpedResponse);
44 | return dumpedResponse;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/model/Message.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.model;
2 |
3 | import com.google.common.base.Objects;
4 | import com.google.common.base.Strings;
5 |
6 | import java.util.Map;
7 |
8 | import static com.google.common.collect.Maps.newHashMap;
9 |
10 | public abstract class Message {
11 | protected String version;
12 | protected String content;
13 | protected Map headers = newHashMap();
14 |
15 | public void setVersion(String version) {
16 | this.version = version;
17 | }
18 |
19 | public void setContent(String content) {
20 | this.content = content;
21 | }
22 |
23 | public void addHeader(String key, String value) {
24 | this.headers.put(key, value);
25 | }
26 |
27 | public String getVersion() {
28 | return version;
29 | }
30 |
31 | public String getContent() {
32 | return content;
33 | }
34 |
35 | public Map getHeaders() {
36 | return this.headers;
37 | }
38 |
39 | public boolean match(Message that) {
40 | return doMatch(version, that.version)
41 | && doMatch(content, that.content)
42 | && doMatch(headers, that.headers);
43 | }
44 |
45 | protected boolean doMatch(Map thisField, Map thatField) {
46 | if (thisField == null || thisField.isEmpty()) {
47 | return true;
48 | }
49 |
50 | for (Map.Entry entry : thisField.entrySet()) {
51 | if (!doMatch(entry.getValue(), thatField.get(entry.getKey()))) {
52 | return false;
53 | }
54 | }
55 |
56 | return true;
57 | }
58 |
59 | protected boolean doMatch(String thisField, String thatField) {
60 | return Strings.isNullOrEmpty(thisField) || thisField.equals(thatField);
61 | }
62 |
63 | @Override
64 | public int hashCode() {
65 | return Objects.hashCode(version, content, headers);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/mount/MountHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.mount;
2 |
3 | import com.github.dreamhead.moco.MocoConfig;
4 | import com.github.dreamhead.moco.ResponseHandler;
5 | import com.github.dreamhead.moco.handler.AbstractContentResponseHandler;
6 | import com.github.dreamhead.moco.util.FileContentType;
7 | import io.netty.buffer.ByteBuf;
8 | import io.netty.handler.codec.http.FullHttpRequest;
9 |
10 | import java.io.File;
11 | import java.io.IOException;
12 |
13 | import static com.google.common.io.Files.toByteArray;
14 |
15 | public class MountHandler extends AbstractContentResponseHandler {
16 | private final MountPathExtractor extractor;
17 |
18 | private final File dir;
19 | private final MountTo target;
20 |
21 | public MountHandler(File dir, MountTo target) {
22 | this.dir = dir;
23 | this.target = target;
24 | this.extractor = new MountPathExtractor(target);
25 | }
26 |
27 | @Override
28 | protected void writeContentResponse(FullHttpRequest request, ByteBuf buffer) {
29 | try {
30 | buffer.writeBytes(toByteArray(targetFile(request)));
31 | } catch (IOException e) {
32 | throw new RuntimeException(e);
33 | }
34 | }
35 |
36 | private File targetFile(FullHttpRequest request) {
37 | String relativePath = extractor.extract(request);
38 | return new File(dir, relativePath);
39 | }
40 |
41 | @Override
42 | protected String getContentType(FullHttpRequest request) {
43 | return new FileContentType(targetFile(request).getName()).getContentType();
44 | }
45 |
46 | @Override
47 | public ResponseHandler apply(final MocoConfig config) {
48 | if (config.isFor("uri")) {
49 | return new MountHandler(this.dir, this.target.apply(config));
50 | }
51 |
52 | if (config.isFor("file")) {
53 | return new MountHandler(new File(config.apply(this.dir.getName())), this.target);
54 | }
55 |
56 | return this;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/runner/monitor/MonitorFactory.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner.monitor;
2 |
3 | import com.github.dreamhead.moco.runner.*;
4 | import com.google.common.collect.Iterables;
5 | import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | import java.io.File;
10 | import java.util.List;
11 |
12 | import static com.google.common.collect.Lists.newArrayList;
13 |
14 | public class MonitorFactory {
15 | private static Logger logger = LoggerFactory.getLogger(MonitorFactory.class);
16 |
17 | public Runner createShutdownMonitor(final Runner runner, final int shutdownPort, final String shutdownKey) {
18 | return new MonitorRunner(runner, new ShutdownMonitor(shutdownPort, shutdownKey, new ShutdownListener() {
19 | @Override
20 | public void onShutdown() {
21 | runner.stop();
22 | }
23 | }));
24 | }
25 |
26 | public FileMonitor createConfigurationMonitor(final File configuration, final FileRunner fileRunner) {
27 | return new FileMonitor(configuration, createListener(fileRunner));
28 | }
29 |
30 | public Monitor createSettingMonitor(final File settingsFile, final Iterable configurationFiles, final FileRunner fileRunner) {
31 | List files = newArrayList(settingsFile);
32 | Iterables.addAll(files, configurationFiles);
33 | return new FilesMonitor(files, createListener(fileRunner));
34 | }
35 |
36 | private FileAlterationListenerAdaptor createListener(final FileRunner fileRunner) {
37 | return new FileAlterationListenerAdaptor() {
38 | @Override
39 | public void onFileChange(File file) {
40 | logger.info("{} change detected.", file.getName());
41 | try {
42 | fileRunner.restart();
43 | } catch (Exception e) {
44 | logger.error("Fail to load configuration in {}.", file.getName());
45 | logger.error(e.getMessage());
46 | }
47 | }
48 | };
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/runner/monitor/FileMonitor.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner.monitor;
2 |
3 | import org.apache.commons.io.monitor.FileAlterationListener;
4 | import org.apache.commons.io.monitor.FileAlterationMonitor;
5 | import org.apache.commons.io.monitor.FileAlterationObserver;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | import java.io.File;
10 | import java.io.FileFilter;
11 |
12 | public class FileMonitor implements Monitor {
13 | public static final int INTERVAL = 1000;
14 | private static Logger logger = LoggerFactory.getLogger(FileMonitor.class);
15 |
16 | private final FileAlterationMonitor monitor;
17 | private boolean running = false;
18 |
19 | public FileMonitor(File file, FileAlterationListener listener) {
20 | this.monitor = monitorFile(file, listener);
21 | }
22 |
23 | public synchronized void startMonitor() {
24 | try {
25 | monitor.start();
26 | running = true;
27 | } catch (Exception e) {
28 | logger.error("Error found.", e);
29 | }
30 | }
31 |
32 | public synchronized void stopMonitor() {
33 | try {
34 | if (monitor != null && running) {
35 | monitor.stop();
36 | running = false;
37 | }
38 | } catch (Exception e) {
39 | logger.error("Error found.", e);
40 | }
41 | }
42 |
43 | private FileAlterationMonitor monitorFile(File file, FileAlterationListener listener) {
44 | File parentFile = file.getParentFile();
45 | File directory = (parentFile == null) ? new File(".") : parentFile;
46 | FileAlterationObserver observer = new FileAlterationObserver(directory, sameFile(file));
47 | observer.addListener(listener);
48 |
49 | return new FileAlterationMonitor(INTERVAL, observer);
50 | }
51 |
52 | private FileFilter sameFile(final File file) {
53 | return new FileFilter() {
54 | @Override
55 | public boolean accept(File detectedFile) {
56 | return file.getName().equals(detectedFile.getName());
57 | }
58 | };
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/runner/FileRunner.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner;
2 |
3 | import com.github.dreamhead.moco.bootstrap.StartArgs;
4 |
5 | import java.io.File;
6 | import java.io.FileInputStream;
7 | import java.io.FileNotFoundException;
8 |
9 | import static com.github.dreamhead.moco.runner.JsonRunner.newJsonRunnerWithStreams;
10 | import static com.google.common.collect.ImmutableList.of;
11 |
12 | public abstract class FileRunner implements Runner {
13 | protected final File file;
14 | protected final int port;
15 | private Runner runner;
16 |
17 | protected abstract Runner createRunner();
18 |
19 | protected FileRunner(File file, int port) {
20 | this.file = file;
21 | this.port = port;
22 | this.runner = createRunner();
23 | }
24 |
25 | public void restart() {
26 | this.runner.stop();
27 | this.runner = createRunner();
28 | this.runner.run();
29 | }
30 |
31 | public Runner getRunner() {
32 | return runner;
33 | }
34 |
35 | @Override
36 | public void run() {
37 | this.runner.run();
38 | }
39 |
40 | @Override
41 | public void stop() {
42 | this.runner.stop();
43 | }
44 |
45 | public static FileRunner createConfigurationFileRunner(final File file, final int port) {
46 | return new FileRunner(file, port) {
47 | @Override
48 | protected Runner createRunner() {
49 | return newJsonRunnerWithStreams(of(toInputStream(file)), port);
50 | }
51 | };
52 | }
53 |
54 | public static FileRunner createSettingFileRunner(final File settingsFile, final StartArgs startArgs) {
55 | return new FileRunner(settingsFile, startArgs.getPort()) {
56 | @Override
57 | protected Runner createRunner() {
58 | return new SettingRunner(toInputStream(file), startArgs);
59 | }
60 | };
61 | }
62 |
63 | private static FileInputStream toInputStream(final File file) {
64 | try {
65 | return new FileInputStream(file);
66 | } catch (FileNotFoundException e) {
67 | throw new RuntimeException(e);
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/parser/model/SessionSetting.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.parser.model;
2 |
3 | import static com.github.dreamhead.moco.MocoMount.to;
4 |
5 | import com.fasterxml.jackson.annotation.JsonAutoDetect;
6 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
7 | import com.github.dreamhead.moco.HttpServer;
8 | import com.github.dreamhead.moco.RequestMatcher;
9 | import com.github.dreamhead.moco.ResponseHandler;
10 | import com.google.common.base.Objects;
11 |
12 | @JsonIgnoreProperties({ "description" })
13 | @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
14 | public class SessionSetting {
15 | private RequestSetting request;
16 | private ResponseSetting response;
17 | private String redirectTo;
18 | private MountSetting mount;
19 |
20 | private boolean isMount() {
21 | return this.mount != null;
22 | }
23 |
24 | private boolean isAnyResponse() {
25 | return request == null;
26 | }
27 |
28 | @Override
29 | public String toString() {
30 | return Objects.toStringHelper(this).omitNullValues().add("request", request).add("response", response).toString();
31 | }
32 |
33 | private boolean isRedirectResponse() {
34 | return redirectTo != null;
35 | }
36 |
37 | private ResponseHandler getResponseHandler() {
38 | if (response == null) {
39 | throw new RuntimeException("No response specified");
40 | }
41 |
42 | return response.getResponseHandler();
43 | }
44 |
45 | private RequestMatcher getRequestMatcher() {
46 | if (request == null) {
47 | throw new RuntimeException("No request specified");
48 | }
49 |
50 | return request.getRequestMatcher();
51 | }
52 |
53 | public void bindTo(HttpServer server) {
54 | if (isMount()) {
55 | server.mount(mount.getDir(), to(mount.getUri()), mount.getMountPredicates());
56 | } else if (isAnyResponse()) {
57 | server.response(getResponseHandler());
58 | } else if (isRedirectResponse()) {
59 | server.request(getRequestMatcher()).redirectTo(redirectTo);
60 | } else {
61 | server.request(getRequestMatcher()).response(getResponseHandler());
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/handler/failover/DefaultRequestTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.handler.failover;
2 |
3 | import com.github.dreamhead.moco.model.DefaultRequest;
4 | import org.junit.Test;
5 |
6 | import static org.hamcrest.CoreMatchers.is;
7 | import static org.junit.Assert.assertThat;
8 |
9 | public class DefaultRequestTest {
10 | @Test
11 | public void should_be_match_if_request_is_same() {
12 | DefaultRequest request = new DefaultRequest();
13 | request.setVersion("HTTP/1.1");
14 | request.setMethod("POST");
15 | request.setContent("proxy");
16 | request.addHeader("Cookie", "loggedIn=true");
17 | request.addHeader("Host", "localhost:12306");
18 |
19 | assertThat(request.match(request), is(true));
20 | }
21 |
22 | @Test
23 | public void should_be_match_if_failover_field_is_null() {
24 | DefaultRequest request = new DefaultRequest();
25 | request.setVersion("HTTP/1.1");
26 | request.setMethod("POST");
27 | request.setContent("proxy");
28 | request.addHeader("Cookie", "loggedIn=true");
29 | request.addHeader("Host", "localhost:12306");
30 |
31 | DefaultRequest failover = new DefaultRequest();
32 | failover.setVersion(null);
33 | failover.setMethod("POST");
34 | failover.setContent("proxy");
35 | failover.addHeader("Cookie", "loggedIn=true");
36 | failover.addHeader("Host", "localhost:12306");
37 |
38 | assertThat(failover.match(failover), is(true));
39 | }
40 |
41 | @Test
42 | public void should_be_match_even_if_target_request_has_more_headers() {
43 | DefaultRequest request = new DefaultRequest();
44 | request.setVersion("HTTP/1.1");
45 | request.setMethod("POST");
46 | request.setContent("proxy");
47 | request.addHeader("Cookie", "loggedIn=true");
48 | request.addHeader("Host", "localhost:12306");
49 |
50 | DefaultRequest failover = new DefaultRequest();
51 | failover.setVersion("HTTP/1.1");
52 | failover.setMethod("POST");
53 | failover.setContent("proxy");
54 | failover.addHeader("Host", "localhost:12306");
55 |
56 | assertThat(failover.match(request), is(true));
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/MocoMatchTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import org.apache.http.client.fluent.Content;
4 | import org.apache.http.client.fluent.Request;
5 | import org.junit.Test;
6 |
7 | import java.io.IOException;
8 |
9 | import static com.github.dreamhead.moco.RemoteTestUtils.remoteUrl;
10 | import static org.hamcrest.CoreMatchers.is;
11 | import static org.junit.Assert.assertThat;
12 |
13 | public class MocoMatchTest extends AbstractMocoStandaloneTest {
14 | @Test
15 | public void should_match_uri() throws IOException {
16 | runWithConfiguration("match.json");
17 | assertThat(helper.get(remoteUrl("/bar/foo")), is("uri_match"));
18 | assertThat(helper.get(remoteUrl("/blah/foo")), is("uri_match"));
19 | }
20 |
21 | @Test
22 | public void should_match_text() throws IOException {
23 | runWithConfiguration("match.json");
24 | assertThat(helper.postContent(remoteUrl("/text-match"), "barfoo"), is("text_match"));
25 | assertThat(helper.postContent(remoteUrl("/text-match"), "blahfoo"), is("text_match"));
26 | }
27 |
28 | @Test
29 | public void should_match_method() throws IOException {
30 | runWithConfiguration("match.json");
31 | assertThat(helper.get(remoteUrl("/method-match")), is("method_match"));
32 | assertThat(helper.postContent(remoteUrl("/method-match"), "blah"), is("method_match"));
33 | }
34 |
35 | @Test
36 | public void should_match_header() throws IOException {
37 | runWithConfiguration("match.json");
38 |
39 | Content jsonContent = Request.Get(remoteUrl("/header-match")).addHeader("Content-type", "application/json").execute().returnContent();
40 | assertThat(jsonContent.asString(), is("header_match"));
41 | Content xmlContent = Request.Get(remoteUrl("/header-match")).addHeader("Content-type", "application/xml").execute().returnContent();
42 | assertThat(xmlContent.asString(), is("header_match"));
43 | }
44 |
45 | @Test
46 | public void should_match_query() throws IOException {
47 | runWithConfiguration("match.json");
48 |
49 | assertThat(helper.get(remoteUrl("/query-match?foo=bar")), is("query_match"));
50 | assertThat(helper.get(remoteUrl("/query-match?foo=blah")), is("query_match"));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/ShutdownTask.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.bootstrap;
2 |
3 | import com.github.dreamhead.moco.internal.MocoClient;
4 | import io.netty.channel.*;
5 | import io.netty.channel.socket.SocketChannel;
6 | import io.netty.handler.codec.string.StringEncoder;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | import java.net.ConnectException;
11 |
12 | import static com.github.dreamhead.moco.bootstrap.ShutdownArgs.parse;
13 |
14 | public class ShutdownTask implements BootstrapTask {
15 | private static Logger logger = LoggerFactory.getLogger(ShutdownTask.class);
16 |
17 | private final MocoClient client = new MocoClient();
18 | private final int defaultShutdownPort;
19 | private final String defaultShutdownKey;
20 |
21 | public ShutdownTask(int defaultShutdownPort, String defaultShutdownKey) {
22 | this.defaultShutdownPort = defaultShutdownPort;
23 | this.defaultShutdownKey = defaultShutdownKey;
24 | }
25 |
26 | @Override
27 | public void run(String[] args) {
28 | ShutdownArgs shutdownArgs = parse(args);
29 | client.run("127.0.0.1", shutdownArgs.getShutdownPort(defaultShutdownPort), new ChannelInitializer() {
30 | @Override
31 | protected void initChannel(SocketChannel ch) throws Exception {
32 | ChannelPipeline pipeline = ch.pipeline();
33 | pipeline.addLast("encoder", new StringEncoder());
34 | pipeline.addLast("handler", new ShutdownHandler());
35 | }
36 | });
37 | }
38 |
39 | private class ShutdownHandler extends ChannelInboundHandlerAdapter {
40 | @Override
41 | public void channelActive(ChannelHandlerContext ctx) throws Exception {
42 | ctx.writeAndFlush(defaultShutdownKey + "\r\n").addListener(ChannelFutureListener.CLOSE);
43 | }
44 |
45 | @Override
46 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
47 | ctx.close();
48 |
49 | if (ConnectException.class.isInstance(cause)) {
50 | logger.error("fail to shutdown, please specify correct shutdown port.");
51 | return;
52 | }
53 |
54 | throw new RuntimeException(cause);
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/MocoTemplateStandaloneTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import org.apache.http.Header;
4 | import org.apache.http.HttpVersion;
5 | import org.apache.http.ProtocolVersion;
6 | import org.apache.http.client.fluent.Request;
7 | import org.junit.Test;
8 |
9 | import java.io.IOException;
10 |
11 | import static com.github.dreamhead.moco.RemoteTestUtils.remoteUrl;
12 | import static org.hamcrest.CoreMatchers.is;
13 | import static org.junit.Assert.assertThat;
14 |
15 | public class MocoTemplateStandaloneTest extends AbstractMocoStandaloneTest {
16 | @Test
17 | public void should_return_content_with_template() throws IOException {
18 | runWithConfiguration("template.json");
19 | assertThat(helper.get(remoteUrl("/template")), is("GET"));
20 | }
21 |
22 | @Test
23 | public void should_return_content_from_file_template() throws IOException {
24 | runWithConfiguration("template.json");
25 | assertThat(helper.get(remoteUrl("/file_template")), is("GET"));
26 | }
27 |
28 | @Test
29 | public void should_return_content_from_path_resource_template() throws IOException {
30 | runWithConfiguration("template.json");
31 | assertThat(helper.get(remoteUrl("/file_template")), is("GET"));
32 | }
33 |
34 | @Test
35 | public void should_return_version_from_template() throws IOException {
36 | runWithConfiguration("template.json");
37 | ProtocolVersion version = Request.Get(remoteUrl("/version_template")).version(HttpVersion.HTTP_1_0).execute().returnResponse().getProtocolVersion();
38 | assertThat(version.toString(), is("HTTP/1.0"));
39 | }
40 |
41 | @Test
42 | public void should_return_header_from_template() throws IOException {
43 | runWithConfiguration("template.json");
44 | Header header = Request.Get(remoteUrl("/header_template")).addHeader("foo", "bar").execute().returnResponse().getFirstHeader("foo");
45 | assertThat(header.getValue(), is("bar"));
46 | }
47 |
48 | @Test
49 | public void should_return_cookie_from_template() throws IOException {
50 | runWithConfiguration("template.json");
51 | Request.Get(remoteUrl("/cookie_template")).execute();
52 | String content = helper.get(remoteUrl("/cookie_template"));
53 | assertThat(content, is("OK"));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/runner/RunnerFactory.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner;
2 |
3 | import com.github.dreamhead.moco.bootstrap.StartArgs;
4 | import com.github.dreamhead.moco.runner.monitor.Monitor;
5 | import com.github.dreamhead.moco.runner.monitor.MonitorFactory;
6 |
7 | import java.io.File;
8 |
9 | import static com.github.dreamhead.moco.runner.FileRunner.createConfigurationFileRunner;
10 | import static com.github.dreamhead.moco.runner.FileRunner.createSettingFileRunner;
11 |
12 | public class RunnerFactory {
13 |
14 | private final MonitorFactory monitorFactory = new MonitorFactory();
15 |
16 | private final int defaultShutdownPort;
17 | private final String defaultShutdownKey;
18 |
19 | public RunnerFactory(int defaultShutdownPort, String defaultShutdownKey) {
20 | this.defaultShutdownPort = defaultShutdownPort;
21 | this.defaultShutdownKey = defaultShutdownKey;
22 | }
23 |
24 | public Runner createRunner(StartArgs startArgs) {
25 | Runner dynamicRunner = createDynamicRunner(startArgs);
26 | return monitorFactory.createShutdownMonitor(dynamicRunner, startArgs.getShutdownPort(defaultShutdownPort), defaultShutdownKey);
27 | }
28 |
29 | private Runner createDynamicRunner(StartArgs startArgs) {
30 | if (startArgs.hasConfigurationFile()) {
31 | return createDynamicConfigurationRunner(startArgs);
32 | }
33 |
34 | return createDynamicSettingRunner(startArgs);
35 | }
36 |
37 | private Runner createDynamicSettingRunner(StartArgs startArgs) {
38 | final File settingsFile = new File(startArgs.getSettings().get());
39 | final FileRunner fileRunner = createSettingFileRunner(settingsFile, startArgs);
40 | final SettingRunner runner = (SettingRunner) fileRunner.getRunner();
41 | Monitor fileMonitor = monitorFactory.createSettingMonitor(settingsFile, runner.getFiles(), fileRunner);
42 | return new MonitorRunner(fileRunner, fileMonitor);
43 | }
44 |
45 | private Runner createDynamicConfigurationRunner(StartArgs startArgs) {
46 | final File configuration = new File(startArgs.getConfigurationFile().get());
47 | final FileRunner fileRunner = createConfigurationFileRunner(configuration, startArgs.getPort());
48 | Monitor fileMonitor = monitorFactory.createConfigurationMonitor(configuration, fileRunner);
49 | return new MonitorRunner(fileRunner, fileMonitor);
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | # [Moco](https://github.com/dreamhead/moco) [](https://travis-ci.org/dreamhead/moco)
8 |
9 | Moco is an easy setup stub framework, mainly focusing on testing and integration, inspired by Mock framework, e.g. [Mockito](http://code.google.com/p/mockito/), and [Playframework](http://www.playframework.com/)
10 |
11 | ## Latest Release
12 | * __0.8.1__
13 |
14 | ## Why
15 | Integration, especially based on HTTP protocol, e.g. web service, REST etc, is wildly used in most of our development.
16 |
17 | In the old days, we just deployed another WAR to an application server, e.g. Jetty or Tomcat etc. As we all know, it's so boring to develop a WAR and deploy it to any application server, even if we use an embeded server. And the WAR needs to be reassembled even if we just want to change a little bit.
18 |
19 | ## Quick Start
20 | * Download [Standalone Moco Runner](http://repo1.maven.org/maven2/com/github/dreamhead/moco-runner/0.8.1/moco-runner-0.8.1-standalone.jar)
21 | * Write your own configuration file to describe your Moco server configuration as follow:
22 | ```json
23 | [
24 | {
25 | "response" :
26 | {
27 | "text" : "Hello, Moco"
28 | }
29 | }
30 | ]
31 | ```
32 |
33 | * Run Moco server
34 | ```shell
35 | java -jar moco-runner--standalone.jar start -p 12306 -c foo.json
36 | ```
37 |
38 | * Now, open your favorite browser to visit http://localhost:12306 and you will see "Hello, Moco".
39 |
40 | ## Documents
41 | * More [Usages](moco-doc/usage.md)
42 | * Detailed [APIs](moco-doc/apis.md)
43 | * [Global Settings](moco-doc/global-settings.md) for multiple configuration files.
44 |
45 | ## Build
46 | Make sure you have JDK and Gradle installed.
47 |
48 | * Clone Moco
49 |
50 | ```shell
51 | git clone git@github.com:dreamhead/moco.git
52 | ```
53 | * Build Moco
54 |
55 | ```shell
56 | gradle build
57 | ```
58 | * Build uberjar
59 |
60 | ```shell
61 | gradle uberjar
62 | ```
63 | * Check code before commit
64 |
65 | ```shell
66 | gradle check
67 | ```
68 |
69 | ## Contributing
70 | Check out what you can help [here](moco-doc/plan.md) if you do not have any existing idea.
71 |
72 | ## Copyright and license
73 | Copyright 2013 ZHENG Ye
74 |
75 | Licensed under MIT License (the "License"); You may obtain a copy of the License in the LICENSE file, or at:
76 |
77 | https://raw.github.com/dreamhead/moco/master/MIT-LICENSE.txt
78 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/MocoFileRootTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import com.github.dreamhead.moco.helper.MocoTestHelper;
4 | import org.apache.http.Header;
5 | import org.apache.http.client.fluent.Request;
6 | import org.junit.Before;
7 | import org.junit.Test;
8 |
9 | import java.io.IOException;
10 |
11 | import static com.github.dreamhead.moco.Moco.*;
12 | import static com.github.dreamhead.moco.MocoMount.to;
13 | import static com.github.dreamhead.moco.RemoteTestUtils.port;
14 | import static com.github.dreamhead.moco.RemoteTestUtils.remoteUrl;
15 | import static com.github.dreamhead.moco.RemoteTestUtils.root;
16 | import static com.github.dreamhead.moco.Runner.running;
17 | import static org.hamcrest.CoreMatchers.is;
18 | import static org.junit.Assert.assertThat;
19 |
20 | public class MocoFileRootTest {
21 | private HttpServer server;
22 | private MocoTestHelper helper;
23 |
24 | @Before
25 | public void setup() {
26 | helper = new MocoTestHelper();
27 | server = httpserver(port(), fileRoot("src/test/resources"));
28 | }
29 |
30 | @Test
31 | public void should_config_file_root() throws Exception {
32 | server.response(file("foo.response"));
33 |
34 | running(server, new Runnable() {
35 | @Override
36 | public void run() throws IOException {
37 | assertThat(helper.get(root()), is("foo.response"));
38 | }
39 | });
40 | }
41 |
42 | @Test
43 | public void should_return_header_from_file_root() throws Exception {
44 | server.response(header("foo", file("foo.response")));
45 |
46 | running(server, new Runnable() {
47 | @Override
48 | public void run() throws IOException {
49 | Header header = Request.Get(root()).execute().returnResponse().getFirstHeader("foo");
50 | assertThat(header.getValue(), is("foo.response"));
51 | }
52 | });
53 | }
54 |
55 | @Test
56 | public void should_return_template_from_file_root() throws Exception {
57 | server.response(template(file("foo.response")));
58 |
59 | running(server, new Runnable() {
60 | @Override
61 | public void run() throws IOException {
62 | assertThat(helper.get(root()), is("foo.response"));
63 | }
64 | });
65 | }
66 |
67 | @Test
68 | public void should_mount_correctly() throws Exception {
69 | server.mount("test", to("/dir"));
70 |
71 | running(server, new Runnable() {
72 | @Override
73 | public void run() throws Exception {
74 | assertThat(helper.get(remoteUrl("/dir/dir.response")), is("response from dir"));
75 | }
76 | });
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/resource/reader/TemplateResourceReader.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.resource.reader;
2 |
3 | import com.github.dreamhead.moco.model.LazyRequest;
4 | import com.github.dreamhead.moco.resource.ContentResource;
5 | import freemarker.cache.StringTemplateLoader;
6 | import freemarker.core.ParseException;
7 | import freemarker.template.*;
8 | import io.netty.handler.codec.http.FullHttpRequest;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | import java.io.ByteArrayOutputStream;
13 | import java.io.IOException;
14 | import java.io.OutputStreamWriter;
15 | import java.io.Writer;
16 |
17 | import static com.google.common.collect.ImmutableMap.of;
18 |
19 | public class TemplateResourceReader implements ContentResourceReader {
20 | private static final Logger logger = LoggerFactory.getLogger(TemplateResourceReader.class);
21 | private static final String TEMPLATE_NAME = "template";
22 |
23 | static {
24 | try {
25 | freemarker.log.Logger.selectLoggerLibrary(freemarker.log.Logger.LIBRARY_NONE);
26 | } catch (ClassNotFoundException e) {
27 | throw new RuntimeException(e);
28 | }
29 | }
30 |
31 | private final ContentResource template;
32 | private final Configuration cfg;
33 |
34 | public TemplateResourceReader(ContentResource template) {
35 | this.template = template;
36 | this.cfg = new Configuration();
37 | this.cfg.setObjectWrapper(new DefaultObjectWrapper());
38 | this.cfg.setDefaultEncoding("UTF-8");
39 | this.cfg.setIncompatibleImprovements(new Version(2, 3, 20));
40 | }
41 |
42 | @Override
43 | public byte[] readFor(FullHttpRequest request) {
44 | StringTemplateLoader templateLoader = new StringTemplateLoader();
45 | String templateSource = new String(this.template.readFor(request));
46 | templateLoader.putTemplate(TEMPLATE_NAME, templateSource);
47 | cfg.setTemplateLoader(templateLoader);
48 |
49 | try {
50 | Template template = cfg.getTemplate("template");
51 | ByteArrayOutputStream stream = new ByteArrayOutputStream();
52 | Writer writer = new OutputStreamWriter(stream);
53 | template.process(of("req", new LazyRequest(request)), writer);
54 | return stream.toByteArray();
55 | } catch (ParseException e) {
56 | logger.info("Template is {}", templateSource);
57 | throw new RuntimeException(e);
58 | } catch (IOException e) {
59 | throw new RuntimeException(e);
60 | } catch (TemplateException e) {
61 | throw new RuntimeException(e);
62 | }
63 | }
64 |
65 | @Override
66 | public String getContentType() {
67 | return template.getContentType();
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/moco-core/src/main/java/com/github/dreamhead/moco/internal/MocoHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.internal;
2 |
3 | import com.github.dreamhead.moco.setting.BaseSetting;
4 | import com.google.common.eventbus.EventBus;
5 | import io.netty.channel.ChannelFuture;
6 | import io.netty.channel.ChannelFutureListener;
7 | import io.netty.channel.ChannelHandlerContext;
8 | import io.netty.channel.SimpleChannelInboundHandler;
9 | import io.netty.handler.codec.http.*;
10 |
11 | import java.util.List;
12 |
13 | public class MocoHandler extends SimpleChannelInboundHandler {
14 | private final EventBus eventBus = new EventBus();
15 |
16 | private final List settings;
17 | private final BaseSetting anySetting;
18 |
19 | public MocoHandler(ActualHttpServer server) {
20 | this.settings = server.getSettings();
21 | this.anySetting = server.getAnySetting();
22 | this.eventBus.register(new MocoEventListener());
23 | }
24 |
25 | @Override
26 | protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest message) throws Exception {
27 | eventBus.post(message);
28 | httpRequestReceived(ctx, message);
29 | }
30 |
31 | private void httpRequestReceived(final ChannelHandlerContext ctx, FullHttpRequest request) {
32 | HttpResponse response = getResponse(request);
33 | eventBus.post(response);
34 | ChannelFuture future = ctx.writeAndFlush(response);
35 | future.addListener(ChannelFutureListener.CLOSE);
36 | }
37 |
38 | private FullHttpResponse getResponse(FullHttpRequest request) {
39 | try {
40 | return doGetHttpResponse(request);
41 | } catch (RuntimeException e) {
42 | eventBus.post(e);
43 | return defaultResponse(request, HttpResponseStatus.BAD_REQUEST);
44 | } catch (Exception e) {
45 | eventBus.post(e);
46 | return defaultResponse(request, HttpResponseStatus.INTERNAL_SERVER_ERROR);
47 | }
48 | }
49 |
50 | private FullHttpResponse doGetHttpResponse(FullHttpRequest request) {
51 | FullHttpResponse response = defaultResponse(request, HttpResponseStatus.OK);
52 |
53 | for (BaseSetting setting : settings) {
54 | if (setting.match(request)) {
55 | setting.writeToResponse(request, response);
56 | return response;
57 | }
58 | }
59 |
60 | if (anySetting.match(request)) {
61 | anySetting.writeToResponse(request, response);
62 | return response;
63 | }
64 |
65 | return defaultResponse(request, HttpResponseStatus.BAD_REQUEST);
66 | }
67 |
68 | private FullHttpResponse defaultResponse(HttpRequest request, HttpResponseStatus status) {
69 | return new DefaultFullHttpResponse(request.getProtocolVersion(), status);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/parser/model/Dynamics.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.parser.model;
2 |
3 | import com.github.dreamhead.moco.Moco;
4 | import com.google.common.base.Predicate;
5 |
6 | import java.lang.reflect.Field;
7 | import java.lang.reflect.Method;
8 | import java.lang.reflect.Modifier;
9 | import java.util.Arrays;
10 |
11 | import static com.google.common.base.Predicates.and;
12 | import static com.google.common.base.Predicates.not;
13 | import static com.google.common.base.Predicates.or;
14 |
15 | public class Dynamics {
16 | protected Predicate isClassField() {
17 | return new Predicate() {
18 | @Override
19 | public boolean apply(Field field) {
20 | return "class".equals(field.getName());
21 | }
22 | };
23 | }
24 |
25 | protected Predicate isFinalField() {
26 | return new Predicate() {
27 | @Override
28 | public boolean apply(Field field) {
29 | return Modifier.isFinal(field.getModifiers());
30 | }
31 | };
32 | }
33 |
34 | protected Predicate fieldExist(final T target) {
35 | return new Predicate() {
36 | @Override
37 | public boolean apply(Field field) {
38 | try {
39 | return field.get(target) != null;
40 | } catch (IllegalAccessException e) {
41 | throw new RuntimeException(e);
42 | }
43 | }
44 | };
45 | }
46 |
47 | protected Iterable getFields(Class> clazz) {
48 | Field[] fields = clazz.getDeclaredFields();
49 | for (Field field : fields) {
50 | field.setAccessible(true);
51 | }
52 | return Arrays.asList(fields);
53 | }
54 |
55 | protected Predicate isValidField(T target) {
56 | return and(not(or(isClassField(), isFinalField())), fieldExist(target));
57 | }
58 |
59 | protected T invokeTarget(String name, Object value, Class clazz) {
60 | return invokeTarget(name, value, clazz, value.getClass());
61 | }
62 |
63 | private T invokeTarget(String name, Object value, Class clazz, Class> argClass) {
64 | try {
65 | Method method = Moco.class.getMethod(name, argClass);
66 | Object result = method.invoke(null, value);
67 | return clazz.cast(result);
68 | } catch (Exception e) {
69 | throw new RuntimeException(e);
70 | }
71 | }
72 |
73 | protected T invokeTarget(String name, int value, Class clazz) {
74 | return invokeTarget(name, value, clazz, Integer.TYPE);
75 | }
76 |
77 | protected T invokeTarget(String name, long value, Class clazz) {
78 | return invokeTarget(name, value, clazz, Long.TYPE);
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/MocoXmlTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import org.junit.Test;
4 |
5 | import java.io.IOException;
6 |
7 | import static com.github.dreamhead.moco.Moco.*;
8 | import static com.github.dreamhead.moco.RemoteTestUtils.root;
9 | import static com.github.dreamhead.moco.Runner.running;
10 | import static org.hamcrest.CoreMatchers.is;
11 | import static org.junit.Assert.assertThat;
12 |
13 | public class MocoXmlTest extends AbstractMocoTest {
14 | @Test
15 | public void should_return_content_based_on_xpath() throws Exception {
16 | server.request(eq(xpath("/request/parameters/id/text()"), "1")).response("foo");
17 | server.request(eq(xpath("/request/parameters/id/text()"), "2")).response("bar");
18 |
19 | running(server, new Runnable() {
20 | @Override
21 | public void run() throws IOException {
22 | assertThat(helper.postFile(root(), "foo.xml"), is("foo"));
23 | assertThat(helper.postFile(root(), "bar.xml"), is("bar"));
24 | }
25 | });
26 | }
27 |
28 | @Test
29 | public void should_return_content_based_on_xpath_with_many_elements() throws Exception {
30 | server.request(eq(xpath("/request/parameters/id/text()"), "2")).response("bar");
31 |
32 | running(server, new Runnable() {
33 | @Override
34 | public void run() throws IOException {
35 | assertThat(helper.postFile(root(), "foobar.xml"), is("bar"));
36 | }
37 | });
38 | }
39 |
40 | @Test
41 | public void should_match_exact_xml() throws Exception {
42 | server.request(xml(file("src/test/resources/foo.xml"))).response("foo");
43 |
44 | running(server, new Runnable() {
45 | @Override
46 | public void run() throws IOException {
47 | assertThat(helper.postFile(root(), "foo.xml"), is("foo"));
48 | }
49 | });
50 | }
51 |
52 | @Test
53 | public void should_match_xml() throws Exception {
54 | server.request(xml(text("1"))).response("foo");
55 |
56 | running(server, new Runnable() {
57 | @Override
58 | public void run() throws IOException {
59 | assertThat(helper.postFile(root(), "foo.xml"), is("foo"));
60 | }
61 | });
62 | }
63 |
64 | @Test(expected = IOException.class)
65 | public void should_throw_exception_for_unknown_content() throws Exception {
66 | server.request(xml(text("1"))).response("foo");
67 |
68 | running(server, new Runnable() {
69 | @Override
70 | public void run() throws IOException {
71 | helper.postContent(root(), "blah");
72 | }
73 | });
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/runner/monitor/ShutdownMonitor.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner.monitor;
2 |
3 | import com.github.dreamhead.moco.internal.MocoServer;
4 | import com.google.common.io.CharStreams;
5 | import com.google.common.io.InputSupplier;
6 | import io.netty.channel.ChannelHandlerContext;
7 | import io.netty.channel.ChannelInitializer;
8 | import io.netty.channel.ChannelPipeline;
9 | import io.netty.channel.SimpleChannelInboundHandler;
10 | import io.netty.channel.socket.SocketChannel;
11 | import io.netty.handler.codec.string.StringDecoder;
12 |
13 | import java.io.IOException;
14 | import java.io.Reader;
15 | import java.io.StringReader;
16 |
17 | public class ShutdownMonitor implements Monitor {
18 | private final MocoServer server = new MocoServer();
19 | private final int shutdownPort;
20 | private final String shutdownKey;
21 | private final ShutdownListener shutdownListener;
22 |
23 | public ShutdownMonitor(int shutdownPort, String shutdownKey, ShutdownListener shutdownListener) {
24 | this.shutdownPort = shutdownPort;
25 | this.shutdownKey = shutdownKey;
26 | this.shutdownListener = shutdownListener;
27 | }
28 |
29 | public void startMonitor() {
30 | server.start(this.shutdownPort, new ChannelInitializer() {
31 | @Override
32 | protected void initChannel(SocketChannel ch) throws Exception {
33 | ChannelPipeline pipeline = ch.pipeline();
34 | pipeline.addLast("decoder", new StringDecoder());
35 | pipeline.addLast("handler", new ShutdownHandler());
36 | }
37 | });
38 | }
39 |
40 | public void stopMonitor() {
41 | server.stop();
42 | }
43 |
44 | private class ShutdownHandler extends SimpleChannelInboundHandler {
45 | @Override
46 | protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
47 | if (shouldShutdown(msg)) {
48 | shutdownListener.onShutdown();
49 | shutdownMonitorSelf();
50 | }
51 | }
52 |
53 | private void shutdownMonitorSelf() {
54 | new Thread(new Runnable() {
55 | @Override
56 | public void run() {
57 | stopMonitor();
58 | }
59 | }).start();
60 | }
61 |
62 | private boolean shouldShutdown(String message) {
63 | try {
64 | return shutdownKey.equals(CharStreams.readFirstLine(toSuppiler(message)));
65 | } catch (IOException e) {
66 | throw new RuntimeException(e);
67 | }
68 | }
69 |
70 | private InputSupplier toSuppiler(final String content) {
71 | return new InputSupplier() {
72 | @Override
73 | public Reader getInput() throws IOException {
74 | return new StringReader(content);
75 | }
76 | };
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/runner/SettingRunner.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner;
2 |
3 | import com.github.dreamhead.moco.bootstrap.StartArgs;
4 | import com.github.dreamhead.moco.parser.GlobalSettingParser;
5 | import com.github.dreamhead.moco.parser.model.GlobalSetting;
6 | import com.google.common.base.Function;
7 | import com.google.common.base.Optional;
8 | import com.google.common.base.Predicate;
9 | import com.google.common.collect.FluentIterable;
10 |
11 | import java.io.File;
12 | import java.io.FileInputStream;
13 | import java.io.FileNotFoundException;
14 | import java.io.InputStream;
15 | import java.util.List;
16 |
17 | import static com.github.dreamhead.moco.runner.JsonRunner.newJsonRunnerWithSetting;
18 | import static com.google.common.collect.FluentIterable.from;
19 |
20 | public class SettingRunner implements Runner {
21 | private static final GlobalSettingParser settingParser = new GlobalSettingParser();
22 | private final int port;
23 | private final List globalSettings;
24 | private final Optional env;
25 | private JsonRunner jsonRunner;
26 | private final FluentIterable files;
27 |
28 | public SettingRunner(InputStream stream, StartArgs args) {
29 | this.port = args.getPort();
30 | this.env = args.getEnv();
31 | this.globalSettings = settingParser.parse(stream);
32 | this.files = from(globalSettings).transform(toFile());
33 | }
34 |
35 | public Iterable getFiles() {
36 | return files;
37 | }
38 |
39 | public void run() {
40 | jsonRunner = newJsonRunnerWithSetting(from(globalSettings).filter(byEnv(this.env)).transform(toRunnerSetting()), port);
41 | jsonRunner.run();
42 | }
43 |
44 | private Predicate super GlobalSetting> byEnv(final Optional env) {
45 |
46 | return new Predicate() {
47 | @Override
48 | public boolean apply(GlobalSetting globalSetting) {
49 | return !env.isPresent() || env.get().equalsIgnoreCase(globalSetting.getEnv());
50 |
51 | }
52 | };
53 | }
54 |
55 |
56 | private Function toRunnerSetting() {
57 | return new Function() {
58 | @Override
59 | public RunnerSetting apply(GlobalSetting setting) {
60 | try {
61 | return new RunnerSetting(new FileInputStream(setting.getInclude()), setting.getContext(), setting.getFileRoot());
62 | } catch (FileNotFoundException e) {
63 | throw new RuntimeException(e);
64 | }
65 | }
66 | };
67 | }
68 |
69 | public void stop() {
70 | jsonRunner.stop();
71 | }
72 |
73 | private Function super GlobalSetting, File> toFile() {
74 | return new Function() {
75 | @Override
76 | public File apply(GlobalSetting input) {
77 | return new File(input.getInclude());
78 | }
79 | };
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/parser/GlobalSettingParserTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.parser;
2 |
3 | import com.github.dreamhead.moco.parser.model.GlobalSetting;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 |
7 | import java.io.InputStream;
8 | import java.util.List;
9 |
10 | import static org.hamcrest.CoreMatchers.is;
11 | import static org.junit.Assert.assertThat;
12 |
13 | public class GlobalSettingParserTest {
14 |
15 | private GlobalSettingParser parser;
16 |
17 | @Before
18 | public void setUp() throws Exception {
19 | parser = new GlobalSettingParser();
20 | }
21 |
22 | @Test
23 | public void should_parse_settings_file() {
24 | InputStream stream = getResourceAsStream("multiple/settings.json");
25 | List globalSettings = parser.parse(stream);
26 |
27 | assertThat(globalSettings.get(0).getInclude(), is("src/test/resources/multiple/foo.json"));
28 | assertThat(globalSettings.get(1).getInclude(), is("src/test/resources/multiple/bar.json"));
29 | }
30 |
31 | private InputStream getResourceAsStream(String filename) {
32 | return GlobalSettingParserTest.class.getClassLoader().getResourceAsStream(filename);
33 | }
34 |
35 | @Test
36 | public void should_parse_settings_file_with_context() {
37 | InputStream stream = getResourceAsStream("multiple/context-settings.json");
38 | List globalSettings = parser.parse(stream);
39 |
40 | assertThat(globalSettings.get(0).getInclude(), is("src/test/resources/multiple/foo.json"));
41 | assertThat(globalSettings.get(0).getContext(), is("/foo"));
42 | assertThat(globalSettings.get(1).getInclude(), is("src/test/resources/multiple/bar.json"));
43 | assertThat(globalSettings.get(1).getContext(), is("/bar"));
44 | }
45 |
46 | @Test
47 | public void should_parse_setting_file_with_file_root() {
48 | InputStream stream = getResourceAsStream("multiple/fileroot-settings.json");
49 | List globalSettings = parser.parse(stream);
50 |
51 | assertThat(globalSettings.get(0).getInclude(), is("src/test/resources/multiple/fileroot.json"));
52 | assertThat(globalSettings.get(0).getContext(), is("/fileroot"));
53 | assertThat(globalSettings.get(0).getFileRoot(), is("src/test/resources/"));
54 | }
55 |
56 | @Test
57 | public void should_parse_setting_file_with_env() {
58 | InputStream stream = getResourceAsStream("multiple/env-settings.json");
59 | List globalSettings = parser.parse(stream);
60 |
61 | assertThat(globalSettings.get(0).getInclude(), is("src/test/resources/multiple/foo.json"));
62 | assertThat(globalSettings.get(0).getContext(), is("/foo"));
63 | assertThat(globalSettings.get(0).getEnv(), is("foo"));
64 | assertThat(globalSettings.get(1).getInclude(), is("src/test/resources/multiple/bar.json"));
65 | assertThat(globalSettings.get(1).getContext(), is("/bar"));
66 | assertThat(globalSettings.get(1).getEnv(), is("bar"));
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/moco-runner/src/test/java/com/github/dreamhead/moco/runner/ShutdownMonitorRunnerTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.runner;
2 |
3 | import com.github.dreamhead.moco.bootstrap.ShutdownTask;
4 | import com.github.dreamhead.moco.bootstrap.StartArgs;
5 | import org.apache.http.conn.HttpHostConnectException;
6 | import org.junit.Before;
7 | import org.junit.Test;
8 |
9 | import java.io.IOException;
10 | import java.io.OutputStream;
11 | import java.net.ConnectException;
12 | import java.net.InetAddress;
13 | import java.net.Socket;
14 |
15 | import static com.github.dreamhead.moco.RemoteTestUtils.port;
16 | import static com.github.dreamhead.moco.RemoteTestUtils.root;
17 | import static org.hamcrest.CoreMatchers.is;
18 | import static org.junit.Assert.assertThat;
19 | import static org.junit.Assert.fail;
20 |
21 | public class ShutdownMonitorRunnerTest extends AbstractRunnerTest {
22 |
23 | private final String SHUTDOWN_MOCO_KEY = "_SHUTDOWN_MOCO_KEY";
24 | private final int SHUTDOWN_PORT = 9527;
25 |
26 | @Before
27 | public void setup() {
28 | RunnerFactory factory = new RunnerFactory(SHUTDOWN_PORT, SHUTDOWN_MOCO_KEY);
29 | runner = factory.createRunner(new StartArgs(port(), SHUTDOWN_PORT, "src/test/resources/foo.json", null, null));
30 | runner.run();
31 | }
32 |
33 | @Test(expected = HttpHostConnectException.class)
34 | public void should_shutdown_runner_by_socket() throws IOException {
35 | try {
36 | assertThat(helper.get(root()), is("foo"));
37 | } catch (HttpHostConnectException e) {
38 | fail(e.getMessage());
39 | }
40 |
41 | shutdownMoco(SHUTDOWN_PORT, SHUTDOWN_MOCO_KEY);
42 | waitChangeHappens();
43 | helper.get(root());
44 | }
45 |
46 | @Test(expected = ConnectException.class)
47 | public void should_stop_runner_directly() throws IOException {
48 | try {
49 | assertThat(helper.get(root()), is("foo"));
50 | } catch (HttpHostConnectException e) {
51 | fail(e.getMessage());
52 | }
53 |
54 | runner.stop();
55 |
56 | shutdownMoco(SHUTDOWN_PORT, SHUTDOWN_MOCO_KEY);
57 | }
58 |
59 | @Test(expected = HttpHostConnectException.class)
60 | public void should_stop_runner_via_shutdown_task() throws IOException {
61 | try {
62 | assertThat(helper.get(root()), is("foo"));
63 | } catch (HttpHostConnectException e) {
64 | fail(e.getMessage());
65 | }
66 |
67 | ShutdownTask task = new ShutdownTask(SHUTDOWN_PORT, SHUTDOWN_MOCO_KEY);
68 | task.run(new String[0]);
69 |
70 | waitChangeHappens();
71 | helper.get(root());
72 | }
73 |
74 | private void shutdownMoco(int shutdownPort, String shutdownMocoKey) throws IOException {
75 | Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), shutdownPort);
76 | socket.setSoLinger(false, 0);
77 |
78 | OutputStream outputStream = socket.getOutputStream();
79 | outputStream.write((shutdownMocoKey + "\r\n").getBytes());
80 |
81 | outputStream.flush();
82 | socket.close();
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/moco-runner/src/main/java/com/github/dreamhead/moco/parser/HttpServerParser.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco.parser;
2 |
3 | import com.fasterxml.jackson.core.Version;
4 | import com.fasterxml.jackson.databind.JsonMappingException;
5 | import com.fasterxml.jackson.databind.Module;
6 | import com.fasterxml.jackson.databind.ObjectMapper;
7 | import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
8 | import com.fasterxml.jackson.databind.module.SimpleModule;
9 | import com.fasterxml.jackson.databind.type.TypeFactory;
10 | import com.github.dreamhead.moco.HttpServer;
11 | import com.github.dreamhead.moco.MocoConfig;
12 | import com.github.dreamhead.moco.internal.ActualHttpServer;
13 | import com.github.dreamhead.moco.parser.model.*;
14 | import org.slf4j.Logger;
15 | import org.slf4j.LoggerFactory;
16 |
17 | import java.io.IOException;
18 | import java.io.InputStream;
19 | import java.util.List;
20 |
21 | import static java.lang.String.format;
22 |
23 | public class HttpServerParser {
24 | private static Logger logger = LoggerFactory.getLogger(HttpServer.class);
25 |
26 | private final ObjectMapper mapper = new ObjectMapper();
27 | private final TypeFactory factory = TypeFactory.defaultInstance();
28 |
29 | public HttpServerParser() {
30 | Module textContainerModule = new SimpleModule("TextContainerModule",
31 | new Version(1, 0, 0, null, null, null))
32 | .addDeserializer(TextContainer.class, new TextContainerDeserializer());
33 | Module proxyContainerModule = new SimpleModule("ProxyContainerModule",
34 | new Version(1, 0, 0, null, null, null))
35 | .addDeserializer(ProxyContainer.class, new ProxyContainerDeserializer());
36 | mapper.registerModule(textContainerModule);
37 | mapper.registerModule(proxyContainerModule);
38 | }
39 |
40 | public HttpServer parseServer(InputStream is, int port, MocoConfig... configs) {
41 | return createHttpServer(readSessions(is), port, configs);
42 | }
43 |
44 | private List readSessions(InputStream is) {
45 | try {
46 | return mapper.readValue(is, factory.constructCollectionType(List.class, SessionSetting.class));
47 | } catch (UnrecognizedPropertyException e) {
48 | logger.info("Unrecognized field: {}", e.getMessage());
49 | throw new RuntimeException(format("Unrecognized field [ %s ], please check!", e.getUnrecognizedPropertyName()));
50 | } catch (JsonMappingException e) {
51 | logger.info("{} {}", e.getMessage(), e.getPathReference());
52 | throw new RuntimeException(e);
53 | } catch (IOException e) {
54 | throw new RuntimeException(e);
55 | }
56 | }
57 |
58 | private HttpServer createHttpServer(List sessionSettings, int port, MocoConfig... configs) {
59 | HttpServer server = new ActualHttpServer(port, configs);
60 | for (SessionSetting session : sessionSettings) {
61 | logger.debug("Parse session: {}", session);
62 |
63 | session.bindTo(server);
64 | }
65 |
66 | return server;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/MocoMountTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import org.apache.http.client.HttpResponseException;
4 | import org.junit.Test;
5 |
6 | import java.io.IOException;
7 |
8 | import static com.github.dreamhead.moco.MocoMount.*;
9 | import static com.github.dreamhead.moco.RemoteTestUtils.remoteUrl;
10 | import static com.github.dreamhead.moco.Runner.running;
11 | import static org.hamcrest.CoreMatchers.is;
12 | import static org.junit.Assert.assertThat;
13 |
14 | public class MocoMountTest extends AbstractMocoTest {
15 |
16 | private static final String MOUNT_DIR = "src/test/resources/test";
17 |
18 | @Test
19 | public void should_mount_dir_to_uri() throws Exception {
20 | server.mount(MOUNT_DIR, to("/dir"));
21 |
22 | running(server, new Runnable() {
23 | @Override
24 | public void run() throws IOException {
25 | assertThat(helper.get(remoteUrl("/dir/dir.response")), is("response from dir"));
26 | }
27 | });
28 | }
29 |
30 | @Test(expected = HttpResponseException.class)
31 | public void should_return_bad_request_for_nonexistence_file() throws Exception {
32 | server.mount(MOUNT_DIR, to("/dir"));
33 |
34 | running(server, new Runnable() {
35 | @Override
36 | public void run() throws IOException {
37 | helper.get(remoteUrl("/dir/unknown.response"));
38 | }
39 | });
40 | }
41 |
42 | @Test
43 | public void should_return_inclusion_file() throws Exception {
44 | server.mount(MOUNT_DIR, to("/dir"), include("*.response"));
45 |
46 | running(server, new Runnable() {
47 | @Override
48 | public void run() throws IOException {
49 | assertThat(helper.get(remoteUrl("/dir/dir.response")), is("response from dir"));
50 | }
51 | });
52 | }
53 |
54 | @Test(expected = HttpResponseException.class)
55 | public void should_not_return_non_inclusion_file() throws Exception {
56 | server.mount(MOUNT_DIR, to("/dir"), include("*.response"));
57 |
58 | running(server, new Runnable() {
59 | @Override
60 | public void run() throws IOException {
61 | helper.get(remoteUrl("/dir/foo.bar"));
62 | }
63 | });
64 | }
65 |
66 | @Test(expected = HttpResponseException.class)
67 | public void should_not_return_exclusion_file() throws Exception {
68 | server.mount(MOUNT_DIR, to("/dir"), exclude("*.response"));
69 |
70 | running(server, new Runnable() {
71 | @Override
72 | public void run() throws IOException {
73 | helper.get(remoteUrl("/dir/dir.response"));
74 | }
75 | });
76 | }
77 |
78 | @Test
79 | public void should_return_non_exclusion_file() throws Exception {
80 | server.mount(MOUNT_DIR, to("/dir"), exclude("*.response"));
81 |
82 | running(server, new Runnable() {
83 | @Override
84 | public void run() throws IOException {
85 | assertThat(helper.get(remoteUrl("/dir/foo.bar")), is("foo.bar"));
86 | }
87 | });
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/moco-core/src/test/java/com/github/dreamhead/moco/MocoContextTest.java:
--------------------------------------------------------------------------------
1 | package com.github.dreamhead.moco;
2 |
3 | import com.github.dreamhead.moco.helper.MocoTestHelper;
4 | import org.apache.http.client.HttpResponseException;
5 | import org.junit.Before;
6 | import org.junit.Test;
7 |
8 | import java.io.IOException;
9 |
10 | import static com.github.dreamhead.moco.Moco.*;
11 | import static com.github.dreamhead.moco.MocoMount.to;
12 | import static com.github.dreamhead.moco.RemoteTestUtils.port;
13 | import static com.github.dreamhead.moco.RemoteTestUtils.remoteUrl;
14 | import static com.github.dreamhead.moco.RemoteTestUtils.root;
15 | import static com.github.dreamhead.moco.Runner.running;
16 | import static org.hamcrest.CoreMatchers.is;
17 | import static org.junit.Assert.assertThat;
18 |
19 | public class MocoContextTest {
20 | private static final String MOUNT_DIR = "src/test/resources/test";
21 |
22 | private HttpServer server;
23 | private MocoTestHelper helper;
24 |
25 | @Before
26 | public void setUp() throws Exception {
27 | helper = new MocoTestHelper();
28 | server = httpserver(port(), context("/context"));
29 | }
30 |
31 | @Test
32 | public void should_config_context() throws Exception {
33 | server.get(by(uri("/foo"))).response("foo");
34 |
35 | running(server, new Runnable() {
36 | @Override
37 | public void run() throws Exception {
38 | assertThat(helper.get(remoteUrl("/context/foo")), is("foo"));
39 | }
40 | });
41 | }
42 |
43 | @Test
44 | public void should_mount_correctly() throws Exception {
45 | server.mount(MOUNT_DIR, to("/dir"));
46 |
47 | running(server, new Runnable() {
48 | @Override
49 | public void run() throws IOException {
50 | assertThat(helper.get(remoteUrl("/context/dir/dir.response")), is("response from dir"));
51 | }
52 | });
53 | }
54 |
55 | @Test
56 | public void should_have_context_even_if_there_is_no_context_configured() throws Exception {
57 | server.response("foo");
58 |
59 | running(server, new Runnable() {
60 | @Override
61 | public void run() throws Exception {
62 | String content = helper.get(remoteUrl("/context"));
63 | assertThat(content, is("foo"));
64 | }
65 | });
66 | }
67 |
68 | @Test(expected = HttpResponseException.class)
69 | public void should_throw_exception_without_context() throws Exception {
70 | server.request(by("foo")).response("foo");
71 |
72 | running(server, new Runnable() {
73 | @Override
74 | public void run() throws Exception {
75 | helper.postContent(root(), "foo");
76 | }
77 | });
78 | }
79 |
80 | @Test(expected = HttpResponseException.class)
81 | public void should_throw_exception_without_context_for_any_response_handler() throws Exception {
82 | server.response("foo");
83 |
84 | running(server, new Runnable() {
85 | @Override
86 | public void run() throws Exception {
87 | helper.get(root());
88 | }
89 | });
90 | }
91 | }
92 |
--------------------------------------------------------------------------------