diff --git a/betamax-core/src/test/groovy/co/freeside/betamax/recorder/SequentialTapeWritingSpec.groovy b/betamax-core/src/test/groovy/co/freeside/betamax/recorder/SequentialTapeWritingSpec.groovy index 17c38165..8b0f0e8e 100644 --- a/betamax-core/src/test/groovy/co/freeside/betamax/recorder/SequentialTapeWritingSpec.groovy +++ b/betamax-core/src/test/groovy/co/freeside/betamax/recorder/SequentialTapeWritingSpec.groovy @@ -21,7 +21,7 @@ import co.freeside.betamax.handler.* import co.freeside.betamax.junit.Betamax import co.freeside.betamax.junit.RecorderRule import co.freeside.betamax.message.Response -import co.freeside.betamax.proxy.jetty.SimpleServer +import co.freeside.betamax.util.server.SimpleServer import co.freeside.betamax.util.message.BasicRequest import co.freeside.betamax.util.server.IncrementingHandler import org.junit.Rule @@ -38,11 +38,11 @@ class SequentialTapeWritingSpec extends Specification { @Shared @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes') def recorder = new Recorder(tapeRoot: tapeRoot) @Rule RecorderRule recorderRule = new RecorderRule(recorder) - @Shared @AutoCleanup('stop') def endpoint = new SimpleServer() + @Shared @AutoCleanup('stop') def endpoint = new SimpleServer(IncrementingHandler) def handler = new DefaultHandlerChain(recorder) void setupSpec() { - endpoint.start(IncrementingHandler) + endpoint.start() } @Betamax(tape = 'sequential tape', mode = WRITE_SEQUENTIAL) diff --git a/betamax-core/src/test/groovy/co/freeside/betamax/recorder/TapeModeSpec.groovy b/betamax-core/src/test/groovy/co/freeside/betamax/recorder/TapeModeSpec.groovy index 796f29ed..535ec3c4 100644 --- a/betamax-core/src/test/groovy/co/freeside/betamax/recorder/TapeModeSpec.groovy +++ b/betamax-core/src/test/groovy/co/freeside/betamax/recorder/TapeModeSpec.groovy @@ -19,7 +19,8 @@ package co.freeside.betamax.recorder import co.freeside.betamax.Recorder import co.freeside.betamax.handler.* import co.freeside.betamax.message.Request -import co.freeside.betamax.proxy.jetty.SimpleServer +import co.freeside.betamax.util.server.HelloHandler +import co.freeside.betamax.util.server.SimpleServer import co.freeside.betamax.util.message.BasicRequest import co.freeside.betamax.util.server.EchoHandler import spock.lang.* @@ -32,12 +33,12 @@ class TapeModeSpec extends Specification { @Shared @AutoCleanup('deleteDir') File tapeRoot = newTempDir('tapes') @Shared Recorder recorder = new Recorder(tapeRoot: tapeRoot) - @Shared @AutoCleanup('stop') SimpleServer endpoint = new SimpleServer() + @Shared @AutoCleanup('stop') SimpleServer endpoint = new SimpleServer(HelloHandler) HttpHandler handler = new DefaultHandlerChain(recorder) Request request = new BasicRequest('GET', endpoint.url) void setupSpec() { - endpoint.start(EchoHandler) + endpoint.start() } void cleanup() { diff --git a/betamax-core/src/test/groovy/co/freeside/betamax/tape/MultiThreadedTapeWritingSpec.groovy b/betamax-core/src/test/groovy/co/freeside/betamax/tape/MultiThreadedTapeWritingSpec.groovy index 97c2f2b3..7db93a7c 100644 --- a/betamax-core/src/test/groovy/co/freeside/betamax/tape/MultiThreadedTapeWritingSpec.groovy +++ b/betamax-core/src/test/groovy/co/freeside/betamax/tape/MultiThreadedTapeWritingSpec.groovy @@ -18,12 +18,10 @@ package co.freeside.betamax.tape import java.util.concurrent.CountDownLatch import co.freeside.betamax.* -import co.freeside.betamax.handler.* -import co.freeside.betamax.junit.Betamax -import co.freeside.betamax.junit.RecorderRule -import co.freeside.betamax.proxy.jetty.SimpleServer +import co.freeside.betamax.handler.DefaultHandlerChain +import co.freeside.betamax.junit.* import co.freeside.betamax.util.message.BasicRequest -import co.freeside.betamax.util.server.HelloHandler +import co.freeside.betamax.util.server.* import org.junit.Rule import spock.lang.* import static co.freeside.betamax.util.FileUtils.newTempDir @@ -37,10 +35,10 @@ class MultiThreadedTapeWritingSpec extends Specification { @Rule RecorderRule recorderRule = new RecorderRule(recorder) def handler = new DefaultHandlerChain(recorder) - @Shared @AutoCleanup('stop') SimpleServer endpoint = new SimpleServer() + @Shared @AutoCleanup('stop') SimpleServer endpoint = new SimpleServer(HelloHandler) void setupSpec() { - endpoint.start(HelloHandler) + endpoint.start() } @Betamax(tape = 'multi_threaded_tape_writing_spec', mode = TapeMode.READ_WRITE) diff --git a/betamax-httpclient/src/test/groovy/co/freeside/betamax/httpclient/BetamaxHttpClientSpec.groovy b/betamax-httpclient/src/test/groovy/co/freeside/betamax/httpclient/BetamaxHttpClientSpec.groovy index 5f8ba19e..96f78920 100644 --- a/betamax-httpclient/src/test/groovy/co/freeside/betamax/httpclient/BetamaxHttpClientSpec.groovy +++ b/betamax-httpclient/src/test/groovy/co/freeside/betamax/httpclient/BetamaxHttpClientSpec.groovy @@ -18,18 +18,15 @@ package co.freeside.betamax.httpclient import co.freeside.betamax.* import co.freeside.betamax.handler.HandlerException -import co.freeside.betamax.junit.Betamax -import co.freeside.betamax.junit.RecorderRule -import co.freeside.betamax.proxy.jetty.SimpleServer -import co.freeside.betamax.util.Network +import co.freeside.betamax.junit.* +import co.freeside.betamax.util.* import co.freeside.betamax.util.server.* -import groovyx.net.http.HttpResponseDecorator -import groovyx.net.http.RESTClient +import groovyx.net.http.* +import io.netty.channel.ChannelInboundHandler import org.apache.http.client.methods.* import org.apache.http.entity.StringEntity import org.apache.http.impl.client.AbstractHttpClient import org.apache.http.params.HttpParams -import org.eclipse.jetty.server.Handler import org.junit.Rule import spock.lang.* import static co.freeside.betamax.util.server.HelloHandler.HELLO_WORLD @@ -40,161 +37,161 @@ import static org.apache.http.entity.ContentType.APPLICATION_FORM_URLENCODED @Issue('https://github.com/robfletcher/betamax/issues/40') class BetamaxHttpClientSpec extends Specification { - @Shared @AutoCleanup('deleteDir') def tapeRoot = co.freeside.betamax.util.FileUtils.newTempDir('tapes') - def recorder = new Recorder(tapeRoot: tapeRoot) + @Shared @AutoCleanup('deleteDir') def tapeRoot = FileUtils.newTempDir('tapes') + def recorder = new Recorder(tapeRoot: tapeRoot) @Rule RecorderRule recorderRule = new RecorderRule(recorder) - @AutoCleanup('stop') def endpoint = new SimpleServer() - def http = new BetamaxHttpClient(recorder) + @AutoCleanup('stop') def endpoint + def http = new BetamaxHttpClient(recorder) - @Betamax(tape = 'betamax http client', mode = TapeMode.READ_WRITE) - void 'can use Betamax without starting the proxy'() { - given: - endpoint.start(HelloHandler) + @Betamax(tape = 'betamax http client', mode = TapeMode.READ_WRITE) + void 'can use Betamax without starting the proxy'() { + given: + endpoint = SimpleServer.start(HelloHandler) - and: - def request = new HttpGet(endpoint.url) + and: + def request = new HttpGet(endpoint.url) - when: - def response = http.execute(request) + when: + def response = http.execute(request) - then: - response.statusLine.statusCode == HTTP_OK - response.entity.content.text == HELLO_WORLD + then: + response.statusLine.statusCode == HTTP_OK + response.entity.content.text == HELLO_WORLD - and: - response.getFirstHeader(VIA).value == 'Betamax' - response.getFirstHeader('X-Betamax').value == 'REC' - } + and: + response.getFirstHeader(VIA).value == 'Betamax' + response.getFirstHeader('X-Betamax').value == 'REC' + } - @Betamax(tape = 'betamax http client', mode = TapeMode.READ_WRITE) - void 'can play back from tape'() { - given: - def handler = Mock(Handler) - endpoint.start(handler) + @Betamax(tape = 'betamax http client', mode = TapeMode.READ_WRITE) + void 'can play back from tape'() { + given: + def handler = Mock(ChannelInboundHandler) + endpoint = SimpleServer.start(handler) - and: - def request = new HttpGet(endpoint.url) + and: + def request = new HttpGet(endpoint.url) - when: - def response = http.execute(request) + when: + def response = http.execute(request) - then: - response.statusLine.statusCode == HTTP_OK - response.entity.content.text == HELLO_WORLD + then: + response.statusLine.statusCode == HTTP_OK + response.entity.content.text == HELLO_WORLD - and: - response.getFirstHeader(VIA).value == 'Betamax' - response.getFirstHeader('X-Betamax').value == 'PLAY' + and: + response.getFirstHeader(VIA).value == 'Betamax' + response.getFirstHeader('X-Betamax').value == 'PLAY' - and: - 0 * handler.handle(*_) - } + and: + 0 * handler.channelRead(* _) + } - @Betamax(tape = 'betamax http client', mode = TapeMode.READ_WRITE) - void 'can send a request with a body'() { - given: - endpoint.start(EchoHandler) - - and: - def request = new HttpPost(endpoint.url) - request.entity = new StringEntity('message=O HAI', APPLICATION_FORM_URLENCODED) - - when: - def response = http.execute(request) - - then: - response.statusLine.statusCode == HTTP_OK - response.entity.content.text.endsWith 'message=O HAI' - - and: - response.getFirstHeader(VIA).value == 'Betamax' - } - - void 'fails in non-annotated spec'() { - given: - def handler = Mock(Handler) - endpoint.start(handler) - - when: - http.execute(new HttpGet(endpoint.url)) - - then: - def e = thrown(HandlerException) - e.message == 'No tape' - - and: - 0 * handler.handle(*_) - } - - @Betamax(tape = 'betamax http client') - void 'can use ignoreLocalhost config setting'() { - given: - endpoint.start(HelloHandler) - - and: - recorder.ignoreLocalhost = true - - and: - def request = new HttpGet(endpoint.url) - - when: - def response = http.execute(request) - - then: - response.statusLine.statusCode == HTTP_OK - response.entity.content.text == HELLO_WORLD - - and: - !response.getFirstHeader(VIA) - !response.getFirstHeader('X-Betamax') - } - - @Betamax(tape = 'betamax http client') - void 'can use ignoreHosts config setting'() { - given: - endpoint.start(HelloHandler) + @Betamax(tape = 'betamax http client', mode = TapeMode.READ_WRITE) + void 'can send a request with a body'() { + given: + endpoint = SimpleServer.start(EchoHandler) + + and: + def request = new HttpPost(endpoint.url) + request.entity = new StringEntity('message=O HAI', APPLICATION_FORM_URLENCODED) + + when: + def response = http.execute(request) + + then: + response.statusLine.statusCode == HTTP_OK + response.entity.content.text.endsWith 'message=O HAI' + + and: + response.getFirstHeader(VIA).value == 'Betamax' + } + + void 'fails in non-annotated spec'() { + given: + def handler = Mock(ChannelInboundHandler) + endpoint = SimpleServer.start(handler) + + when: + http.execute(new HttpGet(endpoint.url)) + + then: + def e = thrown(HandlerException) + e.message == 'No tape' + + and: + 0 * handler.channelRead(* _) + } + + @Betamax(tape = 'betamax http client') + void 'can use ignoreLocalhost config setting'() { + given: + endpoint = SimpleServer.start(HelloHandler) + + and: + recorder.ignoreLocalhost = true + + and: + def request = new HttpGet(endpoint.url) + + when: + def response = http.execute(request) + + then: + response.statusLine.statusCode == HTTP_OK + response.entity.content.text == HELLO_WORLD + + and: + !response.getFirstHeader(VIA) + !response.getFirstHeader('X-Betamax') + } + + @Betamax(tape = 'betamax http client') + void 'can use ignoreHosts config setting'() { + given: + endpoint = SimpleServer.start(HelloHandler) - and: - recorder.ignoreHosts = Network.localAddresses - - and: - def request = new HttpGet(endpoint.url) - - when: - def response = http.execute(request) - - then: - response.statusLine.statusCode == HTTP_OK - response.entity.content.text == HELLO_WORLD - - and: - !response.getFirstHeader(VIA) - !response.getFirstHeader('X-Betamax') - } + and: + recorder.ignoreHosts = Network.localAddresses + + and: + def request = new HttpGet(endpoint.url) + + when: + def response = http.execute(request) + + then: + response.statusLine.statusCode == HTTP_OK + response.entity.content.text == HELLO_WORLD + + and: + !response.getFirstHeader(VIA) + !response.getFirstHeader('X-Betamax') + } - @Betamax(tape = 'betamax http client', mode = TapeMode.READ_WRITE) - void 'can use with HttpBuilder'() { - given: - endpoint.start(HelloHandler) - - and: - def restClient = new RESTClient() { - @Override - protected AbstractHttpClient createClient(HttpParams params) { - new BetamaxHttpClient(recorder) - } - } - - when: - HttpResponseDecorator response = restClient.get(uri: endpoint.url) - - then: - response.status == HTTP_OK - response.data.text == HELLO_WORLD - - and: - response.getFirstHeader(VIA).value == 'Betamax' - response.getFirstHeader('X-Betamax').value == 'PLAY' - } + @Betamax(tape = 'betamax http client', mode = TapeMode.READ_WRITE) + void 'can use with HttpBuilder'() { + given: + endpoint = SimpleServer.start(HelloHandler) + + and: + def restClient = new RESTClient() { + @Override + protected AbstractHttpClient createClient(HttpParams params) { + new BetamaxHttpClient(recorder) + } + } + + when: + HttpResponseDecorator response = restClient.get(uri: endpoint.url) + + then: + response.status == HTTP_OK + response.data.text == HELLO_WORLD + + and: + response.getFirstHeader(VIA).value == 'Betamax' + response.getFirstHeader('X-Betamax').value == 'PLAY' + } } diff --git a/betamax-netty/betamax-netty.gradle b/betamax-netty/betamax-netty.gradle deleted file mode 100644 index 168df7e2..00000000 --- a/betamax-netty/betamax-netty.gradle +++ /dev/null @@ -1,14 +0,0 @@ -apply from: "$rootDir/gradle/javaModule.gradle" -apply from: "$rootDir/gradle/publishedModule.gradle" - -dependencies { - compile project(":betamax-core") - compile commonDependencies.netty -} - -modifyPom { - project { - name "Betamax Netty" - description "The base Netty support classes for Betamax." - } -} \ No newline at end of file diff --git a/betamax-netty/src/main/java/co/freeside/betamax/proxy/netty/NettyBetamaxServer.java b/betamax-netty/src/main/java/co/freeside/betamax/proxy/netty/NettyBetamaxServer.java deleted file mode 100644 index 471f10d3..00000000 --- a/betamax-netty/src/main/java/co/freeside/betamax/proxy/netty/NettyBetamaxServer.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package co.freeside.betamax.proxy.netty; - -import java.net.*; -import io.netty.bootstrap.*; -import io.netty.channel.*; -import io.netty.channel.nio.*; -import io.netty.channel.socket.nio.*; - -/** - * A Netty-based implementation of the Betamax proxy server. - */ -public class NettyBetamaxServer { - - private final int port; - private final ChannelInitializer channelInitializer; - private EventLoopGroup group; - private Channel channel; - - public NettyBetamaxServer(int port, ChannelInitializer channelInitializer) { - this.port = port; - this.channelInitializer = channelInitializer; - } - - public InetSocketAddress run() throws Exception { - group = new NioEventLoopGroup(); - ServerBootstrap bootstrap = new ServerBootstrap(); - bootstrap.group(group) - .channel(NioServerSocketChannel.class) - .childHandler(channelInitializer) - .option(ChannelOption.SO_BACKLOG, 128) - .childOption(ChannelOption.SO_KEEPALIVE, true); - - channel = bootstrap.bind("localhost", port).sync().channel(); - return (InetSocketAddress) channel.localAddress(); - } - - public void shutdown() throws InterruptedException { - if (channel != null) { - channel.close().sync(); - } - if (group != null) { - group.shutdownGracefully().sync(); - } - } - -} \ No newline at end of file diff --git a/betamax-netty/src/test/groovy/co/freeside/betamax/proxy/netty/EchoServerHandler.java b/betamax-netty/src/test/groovy/co/freeside/betamax/proxy/netty/EchoServerHandler.java deleted file mode 100644 index 19ba8e42..00000000 --- a/betamax-netty/src/test/groovy/co/freeside/betamax/proxy/netty/EchoServerHandler.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package co.freeside.betamax.proxy.netty; - -import io.netty.buffer.*; -import io.netty.channel.*; -import io.netty.handler.codec.http.*; -import io.netty.util.*; -import static io.netty.handler.codec.http.HttpHeaders.Names.*; -import static io.netty.handler.codec.http.HttpResponseStatus.*; -import static io.netty.handler.codec.http.HttpVersion.*; - -@ChannelHandler.Sharable -public class EchoServerHandler extends ChannelInboundHandlerAdapter { - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - FullHttpRequest request = (FullHttpRequest) msg; - FullHttpResponse response = new DefaultFullHttpResponse( - HTTP_1_1, - OK, - request.content() - ); - response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); - ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - FullHttpResponse response = new DefaultFullHttpResponse( - HTTP_1_1, - OK, - Unpooled.copiedBuffer(cause.getClass().getSimpleName() + ": " + cause.getMessage(), CharsetUtil.UTF_8) - ); - response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); - ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); - } -} diff --git a/betamax-proxy/src/test/groovy/co/freeside/betamax/AnnotationSpec.groovy b/betamax-proxy/src/test/groovy/co/freeside/betamax/AnnotationSpec.groovy index 3ce64cd6..71e4c163 100644 --- a/betamax-proxy/src/test/groovy/co/freeside/betamax/AnnotationSpec.groovy +++ b/betamax-proxy/src/test/groovy/co/freeside/betamax/AnnotationSpec.groovy @@ -17,7 +17,7 @@ package co.freeside.betamax import co.freeside.betamax.junit.* -import co.freeside.betamax.proxy.jetty.SimpleServer +import co.freeside.betamax.util.server.SimpleServer import co.freeside.betamax.util.httpbuilder.BetamaxRESTClient import co.freeside.betamax.util.server.EchoHandler import groovyx.net.http.* @@ -35,7 +35,7 @@ class AnnotationSpec extends Specification { @Shared @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes') def recorder = new ProxyRecorder(tapeRoot: tapeRoot) @Rule RecorderRule recorderRule = new RecorderRule(recorder) - @AutoCleanup('stop') def endpoint = new SimpleServer() + @AutoCleanup('stop') def endpoint = new SimpleServer(EchoHandler) RESTClient http void setup() { @@ -61,7 +61,7 @@ class AnnotationSpec extends Specification { @Betamax(tape = 'annotation_spec', mode = READ_WRITE) void 'annotated feature can record'() { given: - endpoint.start(EchoHandler) + endpoint.start() when: HttpResponseDecorator response = http.get(path: '/') @@ -85,7 +85,7 @@ class AnnotationSpec extends Specification { void 'can make unproxied request after using annotation'() { given: - endpoint.start(EchoHandler) + endpoint.start() when: HttpResponseDecorator response = http.get(path: '/') diff --git a/betamax-proxy/src/test/groovy/co/freeside/betamax/AnnotationTest.groovy b/betamax-proxy/src/test/groovy/co/freeside/betamax/AnnotationTest.groovy index 69e1eaa4..a3866644 100644 --- a/betamax-proxy/src/test/groovy/co/freeside/betamax/AnnotationTest.groovy +++ b/betamax-proxy/src/test/groovy/co/freeside/betamax/AnnotationTest.groovy @@ -17,10 +17,9 @@ package co.freeside.betamax import co.freeside.betamax.junit.* -import co.freeside.betamax.proxy.jetty.SimpleServer import co.freeside.betamax.util.httpbuilder.BetamaxRESTClient -import co.freeside.betamax.util.server.EchoHandler -import groovyx.net.http.RESTClient +import co.freeside.betamax.util.server.* +import groovyx.net.http.* import org.junit.* import org.junit.runner.RunWith import org.junit.runners.BlockJUnit4ClassRunner @@ -37,7 +36,7 @@ class AnnotationTest { static File tapeRoot = newTempDir('tapes') Recorder recorder = new ProxyRecorder(tapeRoot: tapeRoot, defaultMode: READ_WRITE) @Rule public RecorderRule recorderRule = new RecorderRule(recorder) - SimpleServer endpoint = new SimpleServer() + SimpleServer endpoint = new SimpleServer(EchoHandler) RESTClient http @Before @@ -74,9 +73,9 @@ class AnnotationTest { @Test @Betamax(tape = 'annotation_test', mode = READ_WRITE) void annotatedTestCanRecord() { - endpoint.start(EchoHandler) + endpoint.start() - def response = http.get(path: '/') + HttpResponseDecorator response = http.get(path: '/') assert response.status == HTTP_OK assert response.getFirstHeader(VIA)?.value == 'Betamax' @@ -86,7 +85,7 @@ class AnnotationTest { @Test @Betamax(tape = 'annotation_test', mode = READ_WRITE) void annotatedTestCanPlayBack() { - def response = http.get(path: '/') + HttpResponseDecorator response = http.get(path: '/') assert response.status == HTTP_OK assert response.getFirstHeader(VIA)?.value == 'Betamax' @@ -95,9 +94,9 @@ class AnnotationTest { @Test void canMakeUnproxiedRequestAfterUsingAnnotation() { - endpoint.start(EchoHandler) + endpoint.start() - def response = http.get(path: '/') + HttpResponseDecorator response = http.get(path: '/') assert response.status == HTTP_OK assert response.getFirstHeader(VIA) == null diff --git a/betamax-proxy/src/test/groovy/co/freeside/betamax/LocalhostSpec.groovy b/betamax-proxy/src/test/groovy/co/freeside/betamax/LocalhostSpec.groovy index 7e03e68c..f3aca356 100644 --- a/betamax-proxy/src/test/groovy/co/freeside/betamax/LocalhostSpec.groovy +++ b/betamax-proxy/src/test/groovy/co/freeside/betamax/LocalhostSpec.groovy @@ -18,7 +18,7 @@ package co.freeside.betamax import co.freeside.betamax.junit.Betamax import co.freeside.betamax.junit.RecorderRule -import co.freeside.betamax.proxy.jetty.SimpleServer +import co.freeside.betamax.util.server.SimpleServer import co.freeside.betamax.util.httpbuilder.BetamaxRESTClient import co.freeside.betamax.util.server.EchoHandler import groovyx.net.http.* @@ -38,12 +38,12 @@ class LocalhostSpec extends Specification { def recorder = new ProxyRecorder(tapeRoot: tapeRoot) @Rule RecorderRule recorderRule = new RecorderRule(recorder) - @Shared @AutoCleanup('stop') def endpoint = new SimpleServer() + @Shared @AutoCleanup('stop') def endpoint = new SimpleServer(EchoHandler) @Shared def http = new BetamaxRESTClient() void setupSpec() { - endpoint.start(EchoHandler) + endpoint.start() } @IgnoreIf({ javaVersion >= 1.6 && javaVersion < 1.7 }) diff --git a/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/HttpBuilderSpec.groovy b/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/HttpBuilderSpec.groovy index 4e642ddd..a41385a1 100644 --- a/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/HttpBuilderSpec.groovy +++ b/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/HttpBuilderSpec.groovy @@ -19,7 +19,7 @@ package co.freeside.betamax.compatibility import co.freeside.betamax.* import co.freeside.betamax.httpclient.BetamaxRoutePlanner import co.freeside.betamax.junit.* -import co.freeside.betamax.proxy.jetty.SimpleServer +import co.freeside.betamax.util.server.SimpleServer import co.freeside.betamax.util.httpbuilder.BetamaxRESTClient import co.freeside.betamax.util.server.EchoHandler import groovyx.net.http.* @@ -36,10 +36,10 @@ class HttpBuilderSpec extends Specification { @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes') def recorder = new ProxyRecorder(tapeRoot: tapeRoot) @Rule RecorderRule recorderRule = new RecorderRule(recorder) - @Shared @AutoCleanup('stop') def endpoint = new SimpleServer() + @Shared @AutoCleanup('stop') def endpoint = new SimpleServer(EchoHandler) void setupSpec() { - endpoint.start(EchoHandler) + endpoint.start() } @Timeout(10) diff --git a/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/HttpClient3Spec.groovy b/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/HttpClient3Spec.groovy index 0931d24e..144ac39e 100644 --- a/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/HttpClient3Spec.groovy +++ b/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/HttpClient3Spec.groovy @@ -18,7 +18,7 @@ package co.freeside.betamax.compatibility import co.freeside.betamax.* import co.freeside.betamax.junit.* -import co.freeside.betamax.proxy.jetty.SimpleServer +import co.freeside.betamax.util.server.SimpleServer import co.freeside.betamax.util.server.EchoHandler import org.apache.commons.httpclient.* import org.apache.commons.httpclient.methods.GetMethod @@ -33,10 +33,10 @@ class HttpClient3Spec extends Specification { @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes') def recorder = new ProxyRecorder(tapeRoot: tapeRoot) @Rule RecorderRule recorderRule = new RecorderRule(recorder) - @Shared @AutoCleanup('stop') def endpoint = new SimpleServer() + @Shared @AutoCleanup('stop') def endpoint = new SimpleServer(EchoHandler) void setupSpec() { - endpoint.start(EchoHandler) + endpoint.start() } @Timeout(10) diff --git a/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/HttpClientSpec.groovy b/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/HttpClientSpec.groovy index c77c78d4..e2b1da27 100644 --- a/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/HttpClientSpec.groovy +++ b/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/HttpClientSpec.groovy @@ -19,7 +19,6 @@ package co.freeside.betamax.compatibility import co.freeside.betamax.* import co.freeside.betamax.httpclient.* import co.freeside.betamax.junit.* -import co.freeside.betamax.proxy.jetty.SimpleServer import co.freeside.betamax.util.server.* import org.apache.http.HttpHost import org.apache.http.client.methods.HttpGet @@ -35,85 +34,90 @@ import static org.apache.http.conn.params.ConnRoutePNames.DEFAULT_PROXY class HttpClientSpec extends Specification { - @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes') + @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes') def recorder = new ProxyRecorder(tapeRoot: tapeRoot, defaultMode: WRITE_ONLY, sslSupport: true) @Rule RecorderRule recorderRule = new RecorderRule(recorder) - @Shared @AutoCleanup('stop') def endpoint = new SimpleServer() - @Shared @AutoCleanup('stop') def httpsEndpoint = new SimpleSecureServer(5001) - - void setupSpec() { - endpoint.start(EchoHandler) - httpsEndpoint.start(HelloHandler) - } - - @Timeout(10) - @Betamax(tape = 'http client spec', mode = TapeMode.READ_WRITE) - void 'proxy intercepts HTTPClient connections when using ProxySelectorRoutePlanner'() { - given: - def http = new DefaultHttpClient() - BetamaxRoutePlanner.configure(http) - - when: - def request = new HttpGet(endpoint.url) - def response = http.execute(request) - - then: - response.statusLine.statusCode == HTTP_OK - response.getFirstHeader(VIA)?.value == 'Betamax' - } - - @Timeout(10) - @Betamax(tape = 'http client spec', mode = TapeMode.READ_WRITE) - void 'proxy intercepts HTTPClient connections when explicitly told to'() { - given: - def http = new DefaultHttpClient() - http.params.setParameter(DEFAULT_PROXY, new HttpHost(recorder.proxyHost, recorder.proxyPort, 'http')) - - when: - def request = new HttpGet(endpoint.url) - def response = http.execute(request) - - then: - response.statusLine.statusCode == HTTP_OK - response.getFirstHeader(VIA)?.value == 'Betamax' - } - - @Timeout(10) - @Betamax(tape = 'http client spec', mode = TapeMode.READ_WRITE) - void 'proxy automatically intercepts SystemDefaultHttpClient connections'() { - given: - def http = new SystemDefaultHttpClient() - - when: - def request = new HttpGet(endpoint.url) - def response = http.execute(request) - - then: - response.statusLine.statusCode == HTTP_OK - response.getFirstHeader(VIA)?.value == 'Betamax' - } - - @Betamax(tape = 'http client spec') - void 'proxy can intercept HTTPS requests'() { - given: - def http = new DefaultHttpClient() - BetamaxRoutePlanner.configure(http) - BetamaxHttpsSupport.configure(http) - - when: 'an HTTPS request is made' - def request = new HttpGet(httpsEndpoint.url) - def response = http.execute(request) - - and: 'we read the response body' - def responseBytes = new ByteArrayOutputStream() - response.entity.writeTo(responseBytes) - def responseString = responseBytes.toString('UTF-8') - - then: 'the request is intercepted by the proxy' - response.statusLine.statusCode == SC_OK - response.getFirstHeader(VIA)?.value == 'Betamax' - - and: 'the response is decoded' - responseString == 'Hello World!' - } + @Shared @AutoCleanup('stop') def endpoint = new SimpleServer(EchoHandler) + @Shared @AutoCleanup('stop') def httpsEndpoint = new SimpleSecureServer(5001, HelloHandler) + + void setupSpec() { + endpoint.start() + httpsEndpoint.start() + } + + @Timeout(10) + @Betamax(tape = 'http client spec', mode = TapeMode.READ_WRITE) + void 'proxy intercepts HTTPClient connections when using ProxySelectorRoutePlanner'() { + given: + def http = new DefaultHttpClient() + BetamaxRoutePlanner.configure(http) + + when: + def request = new HttpGet(endpoint.url) + + def response = http.execute(request) + + then: + response.statusLine.statusCode == HTTP_OK + response.getFirstHeader(VIA)?.value == 'Betamax' + } + + @Timeout(10) + @Betamax(tape = 'http client spec', mode = TapeMode.READ_WRITE) + void 'proxy intercepts HTTPClient connections when explicitly told to'() { + given: + def http = new DefaultHttpClient() + http.params.setParameter(DEFAULT_PROXY, new HttpHost(recorder.proxyHost, recorder.proxyPort, 'http')) + + when: + def request = new HttpGet(endpoint.url) + def response = http.execute(request) + + then: + response.statusLine.statusCode == HTTP_OK + response.getFirstHeader(VIA)?.value == 'Betamax' + } + + @Timeout(10) + @Betamax(tape = 'http client spec', mode = TapeMode.READ_WRITE) + void 'proxy automatically intercepts SystemDefaultHttpClient connections'() { + given: + def http = new SystemDefaultHttpClient() + + when: + def request = new HttpGet(endpoint.url) + def response = http.execute(request) + + then: + response.statusLine.statusCode == HTTP_OK + response.getFirstHeader(VIA)?.value == 'Betamax' + } + + @Betamax(tape = 'http client spec') + void 'proxy can intercept HTTPS requests'() { + given: + def http = new DefaultHttpClient() + BetamaxRoutePlanner.configure(http) + BetamaxHttpsSupport.configure(http) + + when: + 'an HTTPS request is made' + def request = new HttpGet(httpsEndpoint.url) + def response = http.execute(request) + + and: + 'we read the response body' + def responseBytes = new ByteArrayOutputStream() + response.entity.writeTo(responseBytes) + def responseString = responseBytes.toString('UTF-8') + + then: + 'the request is intercepted by the proxy' + response.statusLine.statusCode == SC_OK + response.getFirstHeader(VIA)?.value == 'Betamax' + + and: + 'the response is decoded' + responseString == 'Hello World!' + } } diff --git a/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/HttpURLConnectionSpec.groovy b/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/HttpURLConnectionSpec.groovy index d45a1146..c8494893 100644 --- a/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/HttpURLConnectionSpec.groovy +++ b/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/HttpURLConnectionSpec.groovy @@ -18,7 +18,6 @@ package co.freeside.betamax.compatibility import co.freeside.betamax.* import co.freeside.betamax.junit.* -import co.freeside.betamax.proxy.jetty.SimpleServer import co.freeside.betamax.util.server.* import org.junit.Rule import spock.lang.* @@ -30,40 +29,40 @@ import static org.apache.http.HttpStatus.SC_OK class HttpURLConnectionSpec extends Specification { - @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes') + @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes') def recorder = new ProxyRecorder(tapeRoot: tapeRoot, defaultMode: WRITE_ONLY, sslSupport: true) @Rule RecorderRule recorderRule = new RecorderRule(recorder) - @Shared @AutoCleanup('stop') def endpoint = new SimpleServer() - @Shared @AutoCleanup('stop') def httpsEndpoint = new SimpleSecureServer(5001) + @Shared @AutoCleanup('stop') def endpoint = new SimpleServer(EchoHandler) + @Shared @AutoCleanup('stop') def httpsEndpoint = new SimpleSecureServer(5001, HelloHandler) - void setupSpec() { - endpoint.start(EchoHandler) - httpsEndpoint.start(HelloHandler) - } + void setupSpec() { + endpoint.start() + httpsEndpoint.start() + } - @Timeout(10) - @Betamax(tape = 'http url connection spec', mode = TapeMode.READ_WRITE) - void 'proxy intercepts URL connections'() { - given: - HttpURLConnection connection = new URL(endpoint.url).openConnection() - connection.connect() + @Timeout(10) + @Betamax(tape = 'http url connection spec', mode = TapeMode.READ_WRITE) + void 'proxy intercepts URL connections'() { + given: + HttpURLConnection connection = new URL(endpoint.url).openConnection() + connection.connect() - expect: - connection.responseCode == HTTP_OK - connection.getHeaderField(VIA) == 'Betamax' + expect: + connection.responseCode == HTTP_OK + connection.getHeaderField(VIA) == 'Betamax' - cleanup: - connection.disconnect() - } + cleanup: + connection.disconnect() + } - @Betamax(tape = 'http url connection spec', mode = WRITE_ONLY) - void 'proxy intercepts HTTPS requests'() { - when: - HttpURLConnection connection = httpsEndpoint.url.toURL().openConnection() + @Betamax(tape = 'http url connection spec', mode = WRITE_ONLY) + void 'proxy intercepts HTTPS requests'() { + when: + HttpURLConnection connection = httpsEndpoint.url.toURL().openConnection() - then: - connection.responseCode == SC_OK - connection.getHeaderField(VIA) == 'Betamax' - connection.inputStream.text == 'Hello World!' - } + then: + connection.responseCode == SC_OK + connection.getHeaderField(VIA) == 'Betamax' + connection.inputStream.text == 'Hello World!' + } } diff --git a/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/WsLiteSpec.groovy b/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/WsLiteSpec.groovy index 26146750..50772d96 100644 --- a/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/WsLiteSpec.groovy +++ b/betamax-proxy/src/test/groovy/co/freeside/betamax/compatibility/WsLiteSpec.groovy @@ -18,7 +18,6 @@ package co.freeside.betamax.compatibility import co.freeside.betamax.ProxyRecorder import co.freeside.betamax.junit.* -import co.freeside.betamax.proxy.jetty.SimpleServer import co.freeside.betamax.util.server.* import org.junit.Rule import spock.lang.* @@ -31,46 +30,53 @@ import static org.apache.http.HttpHeaders.VIA class WsLiteSpec extends Specification { - @Shared @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes') + @Shared @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes') def recorder = new ProxyRecorder(tapeRoot: tapeRoot, defaultMode: WRITE_ONLY, sslSupport: true) @Rule RecorderRule recorderRule = new RecorderRule(recorder) - @Shared @AutoCleanup('stop') def endpoint = new SimpleServer() - @Shared @AutoCleanup('stop') def httpsEndpoint = new SimpleSecureServer(5001) + @Shared @AutoCleanup('stop') def endpoint = new SimpleServer(EchoHandler) + @Shared @AutoCleanup('stop') def httpsEndpoint = new SimpleSecureServer(5001, HelloHandler) - void setupSpec() { - endpoint.start(EchoHandler) - httpsEndpoint.start(HelloHandler) - } + void setupSpec() { + endpoint.start() + httpsEndpoint.start() + } - @Betamax(tape = 'wslite spec', mode = READ_WRITE) - void 'can record a connection made with WsLite'() { - given: 'a properly configured wslite instance' - def http = new RESTClient(endpoint.url) + @Betamax(tape = 'wslite spec', mode = READ_WRITE) + void 'can record a connection made with WsLite'() { + given: + 'a properly configured wslite instance' + def http = new RESTClient(endpoint.url) - when: 'a request is made' - def response = http.get(path: '/', proxy: recorder.proxy) + when: + 'a request is made' + def response = http.get(path: '/', proxy: recorder.proxy) - then: 'the request is intercepted' - response.statusCode == HTTP_OK - response.headers[VIA] == 'Betamax' - response.headers[X_BETAMAX] == 'REC' - } + then: + 'the request is intercepted' + response.statusCode == HTTP_OK + response.headers[VIA] == 'Betamax' + response.headers[X_BETAMAX] == 'REC' + } - @Betamax(tape = 'wslite spec', mode = READ_WRITE) - void 'proxy intercepts HTTPS requests'() { - given: 'a properly configured wslite instance' - def http = new RESTClient(httpsEndpoint.url) + @Betamax(tape = 'wslite spec', mode = READ_WRITE) + void 'proxy intercepts HTTPS requests'() { + given: + 'a properly configured wslite instance' + def http = new RESTClient(httpsEndpoint.url) - when: 'a request is made' - def response = http.get(path: '/', proxy: recorder.proxy) + when: + 'a request is made' + def response = http.get(path: '/', proxy: recorder.proxy) - then: 'the request is intercepted' - response.statusCode == HTTP_OK - response.headers[VIA] == 'Betamax' - response.headers[X_BETAMAX] == 'REC' + then: + 'the request is intercepted' + response.statusCode == HTTP_OK + response.headers[VIA] == 'Betamax' + response.headers[X_BETAMAX] == 'REC' - and: 'the response body is decoded' - response.contentAsString == 'Hello World!' - } + and: + 'the response body is decoded' + response.contentAsString == 'Hello World!' + } } diff --git a/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/CustomSecureSocketFactorySpec.groovy b/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/CustomSecureSocketFactorySpec.groovy index 9554480f..8bea0475 100644 --- a/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/CustomSecureSocketFactorySpec.groovy +++ b/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/CustomSecureSocketFactorySpec.groovy @@ -19,7 +19,6 @@ package co.freeside.betamax.proxy import java.security.KeyStore import co.freeside.betamax.ProxyRecorder import co.freeside.betamax.httpclient.BetamaxHttpsSupport -import co.freeside.betamax.proxy.jetty.SimpleServer import co.freeside.betamax.proxy.ssl.DummySSLSocketFactory import co.freeside.betamax.util.server.* import org.apache.http.client.HttpClient @@ -34,8 +33,8 @@ import static org.apache.http.HttpStatus.SC_OK @Issue('https://github.com/robfletcher/betamax/issues/72') class CustomSecureSocketFactorySpec extends Specification { - @Shared @AutoCleanup('deleteDir') File tapeRoot = newTempDir('tapes') - @Shared @AutoCleanup('stop') SimpleServer httpsEndpoint = new SimpleSecureServer(5001) + @Shared @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes') + @Shared @AutoCleanup('stop') def httpsEndpoint = new SimpleSecureServer(5001, HelloHandler) @Shared KeyStore trustStore @Shared URI httpsUri @@ -43,7 +42,7 @@ class CustomSecureSocketFactorySpec extends Specification { HttpClient http void setupSpec() { - httpsEndpoint.start(HelloHandler) + httpsEndpoint.start() httpsUri = httpsEndpoint.url.toURI() diff --git a/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/HttpsSpec.groovy b/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/HttpsSpec.groovy index b5afdc4b..63fe08b8 100644 --- a/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/HttpsSpec.groovy +++ b/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/HttpsSpec.groovy @@ -19,7 +19,6 @@ package co.freeside.betamax.proxy import co.freeside.betamax.* import co.freeside.betamax.httpclient.BetamaxHttpsSupport import co.freeside.betamax.junit.* -import co.freeside.betamax.proxy.jetty.SimpleServer import co.freeside.betamax.util.server.* import org.apache.http.client.HttpClient import org.apache.http.client.methods.HttpGet @@ -35,66 +34,69 @@ import static org.apache.http.HttpStatus.SC_OK @Unroll class HttpsSpec extends Specification { - @Shared @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes') + @Shared @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes') @AutoCleanup('ejectTape') def recorder = new ProxyRecorder(tapeRoot: tapeRoot, sslSupport: true) @Rule RecorderRule recorderRule = new RecorderRule(recorder) - @Shared @AutoCleanup('stop') def httpsEndpoint = new SimpleSecureServer(5001) - @Shared @AutoCleanup('stop') def httpEndpoint = new SimpleServer() - - @Shared URI httpUri - @Shared URI httpsUri - - HttpClient http - - void setupSpec() { - httpEndpoint.start(HelloHandler) - httpsEndpoint.start(HelloHandler) - - httpUri = httpEndpoint.url.toURI() - httpsUri = httpsEndpoint.url.toURI() - } - - void setup() { - http = new SystemDefaultHttpClient() - BetamaxHttpsSupport.configure(http) - } - - @Betamax(tape = 'https spec') - void 'proxy is selected for #scheme URIs'() { - given: - def proxySelector = ProxySelector.default - - expect: - def proxy = proxySelector.select(uri).first() - proxy.type() == Proxy.Type.HTTP - - and: - def proxyURI = "${scheme}://${proxy.address()}".toURI() - InetAddress.getByName(proxyURI.host) == InetAddress.getByName(recorder.proxyHost) - proxyURI.port == recorder.proxyPort - - where: - uri << [httpUri, httpsUri] - scheme = uri.scheme - } - - @Betamax(tape = 'https spec', mode = TapeMode.WRITE_ONLY) - void 'proxy can intercept #scheme requests'() { - when: 'a request is made' - def request = new HttpGet(url) - def response = http.execute(request) - - then: 'it is intercepted by the proxy' - response.statusLine.statusCode == SC_OK - response.getFirstHeader(VIA)?.value == 'Betamax' - - and: 'the response body is readable' - response.entity.content.text == HELLO_WORLD - - where: - url << [httpEndpoint.url, httpsEndpoint.url] - scheme = url.toURI().scheme - } + @Shared @AutoCleanup('stop') def httpsEndpoint = new SimpleSecureServer(5001, HelloHandler) + @Shared @AutoCleanup('stop') def httpEndpoint = new SimpleServer(HelloHandler) + + @Shared URI httpUri + @Shared URI httpsUri + + HttpClient http + + void setupSpec() { + httpEndpoint.start() + httpsEndpoint.start() + + httpUri = httpEndpoint.url.toURI() + httpsUri = httpsEndpoint.url.toURI() + } + + void setup() { + http = new SystemDefaultHttpClient() + BetamaxHttpsSupport.configure(http) + } + + @Betamax(tape = 'https spec') + void 'proxy is selected for #scheme URIs'() { + given: + def proxySelector = ProxySelector.default + + expect: + def proxy = proxySelector.select(uri).first() + proxy.type() == Proxy.Type.HTTP + + and: + def proxyURI = "${scheme}://${proxy.address()}".toURI() + InetAddress.getByName(proxyURI.host) == InetAddress.getByName(recorder.proxyHost) + proxyURI.port == recorder.proxyPort + + where: + uri << [httpUri, httpsUri] + scheme = uri.scheme + } + + @Betamax(tape = 'https spec', mode = TapeMode.WRITE_ONLY) + void 'proxy can intercept #scheme requests'() { + when: + 'a request is made' + def request = new HttpGet(url) + def response = http.execute(request) + + then: + 'it is intercepted by the proxy' + response.statusLine.statusCode == SC_OK + response.getFirstHeader(VIA)?.value == 'Betamax' + + and: + 'the response body is readable' + response.entity.content.text == HELLO_WORLD + + where: + url << [httpEndpoint.url, httpsEndpoint.url] + scheme = url.toURI().scheme + } } diff --git a/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/IgnoreHostsSpec.groovy b/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/IgnoreHostsSpec.groovy index f1295cf0..c912f048 100644 --- a/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/IgnoreHostsSpec.groovy +++ b/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/IgnoreHostsSpec.groovy @@ -17,10 +17,9 @@ package co.freeside.betamax.proxy import co.freeside.betamax.* -import co.freeside.betamax.proxy.jetty.* import co.freeside.betamax.util.httpbuilder.BetamaxRESTClient -import co.freeside.betamax.util.server.EchoHandler -import groovyx.net.http.RESTClient +import co.freeside.betamax.util.server.* +import groovyx.net.http.* import spock.lang.* import static co.freeside.betamax.util.FileUtils.newTempDir import static org.apache.http.HttpHeaders.VIA @@ -30,13 +29,13 @@ import static org.apache.http.HttpHeaders.VIA class IgnoreHostsSpec extends Specification { @Shared @AutoCleanup('deleteDir') File tapeRoot = newTempDir('tapes') - @Shared @AutoCleanup('stop') SimpleServer endpoint = new SimpleServer() + @Shared @AutoCleanup('stop') SimpleServer endpoint = new SimpleServer(EchoHandler) @AutoCleanup('ejectTape') Recorder recorder = new ProxyRecorder(tapeRoot: tapeRoot) @AutoCleanup('stop') ProxyServer proxy = new ProxyServer(recorder) RESTClient http void setupSpec() { - endpoint.start(EchoHandler) + endpoint.start() } void setup() { @@ -51,7 +50,7 @@ class IgnoreHostsSpec extends Specification { proxy.start() when: 'a request is made' - def response = http.get(uri: requestURI) + HttpResponseDecorator response = http.get(uri: requestURI) then: 'the request is not intercepted by the proxy' !response.headers[VIA] @@ -73,7 +72,7 @@ class IgnoreHostsSpec extends Specification { proxy.start() when: 'a request is made' - def response = http.get(uri: requestURI) + HttpResponseDecorator response = http.get(uri: requestURI) then: 'the request is not intercepted by the proxy' !response.headers[VIA] diff --git a/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/NoTapeSpec.groovy b/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/NoTapeSpec.groovy index 17dffcc1..e75e4134 100644 --- a/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/NoTapeSpec.groovy +++ b/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/NoTapeSpec.groovy @@ -16,34 +16,33 @@ package co.freeside.betamax.proxy -import co.freeside.betamax.* -import co.freeside.betamax.proxy.jetty.* +import co.freeside.betamax.ProxyRecorder import co.freeside.betamax.util.httpbuilder.BetamaxRESTClient -import co.freeside.betamax.util.server.EchoHandler -import groovyx.net.http.* +import co.freeside.betamax.util.server.* +import groovyx.net.http.HttpResponseException import spock.lang.* import static java.net.HttpURLConnection.HTTP_FORBIDDEN @Issue('https://github.com/robfletcher/betamax/issues/18') class NoTapeSpec extends Specification { - @Shared Recorder recorder = new ProxyRecorder() - @Shared @AutoCleanup('stop') ProxyServer proxy = new ProxyServer(recorder) - @Shared @AutoCleanup('stop') SimpleServer endpoint = new SimpleServer() - RESTClient http = new BetamaxRESTClient(endpoint.url) + @Shared def recorder = new ProxyRecorder() + @Shared @AutoCleanup('stop') def proxy = new ProxyServer(recorder) + @Shared @AutoCleanup('stop') def endpoint = new SimpleServer(EchoHandler) + def http = new BetamaxRESTClient(endpoint.url) - void setupSpec() { - proxy.start() - endpoint.start(EchoHandler) - } + void setupSpec() { + proxy.start() + endpoint.start() + } - void 'an error is returned if the proxy intercepts a request when no tape is inserted'() { - when: - http.get(path: '/') + void 'an error is returned if the proxy intercepts a request when no tape is inserted'() { + when: + http.get(path: '/') - then: - def e = thrown(HttpResponseException) - e.statusCode == HTTP_FORBIDDEN - e.message == 'No tape' - } + then: + def e = thrown(HttpResponseException) + e.statusCode == HTTP_FORBIDDEN + e.message == 'No tape' + } } diff --git a/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/PreExistingProxySpec.groovy b/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/PreExistingProxySpec.groovy index b89bd9cb..3acfa853 100644 --- a/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/PreExistingProxySpec.groovy +++ b/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/PreExistingProxySpec.groovy @@ -18,8 +18,7 @@ package co.freeside.betamax.proxy import co.freeside.betamax.* import co.freeside.betamax.junit.* -import co.freeside.betamax.proxy.jetty.SimpleServer -import co.freeside.betamax.util.server.HelloHandler +import co.freeside.betamax.util.server.* import org.junit.Rule import spock.lang.* import static co.freeside.betamax.util.FileUtils.newTempDir @@ -30,36 +29,36 @@ import static org.apache.http.HttpHeaders.VIA @Issue('https://github.com/robfletcher/betamax/issues/54') class PreExistingProxySpec extends Specification { - @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes') + @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes') def recorder = new ProxyRecorder(tapeRoot: tapeRoot) @Rule RecorderRule recorderRule = new RecorderRule(recorder) - @Shared @AutoCleanup('stop') def proxyServer = new SimpleServer() + @Shared @AutoCleanup('stop') def proxyServer = new SimpleServer(HelloHandler) - void setupSpec() { - proxyServer.start(HelloHandler) - System.properties.'http.proxyHost' = InetAddress.localHost.hostAddress - System.properties.'http.proxyPort' = proxyServer.port.toString() - } + void setupSpec() { + proxyServer.start() + System.properties.'http.proxyHost' = "localhost" + System.properties.'http.proxyPort' = proxyServer.port.toString() + } - void cleanupSpec() { - System.clearProperty 'http.proxyHost' - System.clearProperty 'http.proxyPort' - } + void cleanupSpec() { + System.clearProperty 'http.proxyHost' + System.clearProperty 'http.proxyPort' + } - @Timeout(10) - @Betamax(tape = 'existing proxy spec', mode = TapeMode.READ_WRITE) - void 'pre-existing proxy settings are used for the outbound request from the Betamax proxy'() { - given: - HttpURLConnection connection = new URL('http://freeside.co/betamax').openConnection() - connection.connect() + @Timeout(10) + @Betamax(tape = 'existing proxy spec', mode = TapeMode.READ_WRITE) + void 'pre-existing proxy settings are used for the outbound request from the Betamax proxy'() { + given: + HttpURLConnection connection = new URL('http://freeside.co/betamax').openConnection() + connection.connect() - expect: - connection.responseCode == HTTP_OK - connection.getHeaderField(VIA) == 'Betamax' - connection.inputStream.text == HELLO_WORLD + expect: + connection.responseCode == HTTP_OK + connection.getHeaderField(VIA) == 'Betamax' + connection.inputStream.text == HELLO_WORLD - cleanup: - connection.disconnect() - } + cleanup: + connection.disconnect() + } } diff --git a/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/ProxyRecordAndPlaybackSpec.groovy b/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/ProxyRecordAndPlaybackSpec.groovy index 990f8352..8f4d19c9 100644 --- a/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/ProxyRecordAndPlaybackSpec.groovy +++ b/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/ProxyRecordAndPlaybackSpec.groovy @@ -17,9 +17,8 @@ package co.freeside.betamax.proxy import co.freeside.betamax.ProxyRecorder -import co.freeside.betamax.proxy.jetty.SimpleServer import co.freeside.betamax.util.httpbuilder.BetamaxRESTClient -import co.freeside.betamax.util.server.HelloHandler +import co.freeside.betamax.util.server.* import groovyx.net.http.* import org.yaml.snakeyaml.Yaml import spock.lang.* @@ -33,7 +32,7 @@ class ProxyRecordAndPlaybackSpec extends Specification { @Shared @AutoCleanup('deleteDir') File tapeRoot = newTempDir('tapes') @Shared @AutoCleanup('ejectTape') ProxyRecorder recorder = new ProxyRecorder(tapeRoot: tapeRoot) @Shared @AutoCleanup('stop') ProxyServer proxy = new ProxyServer(recorder) - @AutoCleanup('stop') SimpleServer endpoint = new SimpleServer() + @AutoCleanup('stop') SimpleServer endpoint = new SimpleServer(HelloHandler) RESTClient http = new BetamaxRESTClient(endpoint.url) void setupSpec() { @@ -44,7 +43,7 @@ class ProxyRecordAndPlaybackSpec extends Specification { @Timeout(10) void 'proxy makes a real HTTP request the first time it gets a request for a URI'() { given: - endpoint.start(HelloHandler) + endpoint.start() when: HttpResponseDecorator response = http.get(path: '/') @@ -73,7 +72,7 @@ class ProxyRecordAndPlaybackSpec extends Specification { @Timeout(10) void 'subsequent requests with a different HTTP method are recorded separately'() { given: - endpoint.start(HelloHandler) + endpoint.start() when: HttpResponseDecorator response = http.post(path: '/') @@ -137,7 +136,7 @@ interactions: @Timeout(10) void 'can play back a loaded tape'() { when: - def response = http.get(uri: 'http://icanhascheezburger.com/') + HttpResponseDecorator response = http.get(uri: 'http://icanhascheezburger.com/') then: response.statusLine.statusCode == HTTP_OK diff --git a/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/ProxyTimeoutSpec.groovy b/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/ProxyTimeoutSpec.groovy index 049e63ed..1adc8225 100644 --- a/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/ProxyTimeoutSpec.groovy +++ b/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/ProxyTimeoutSpec.groovy @@ -18,7 +18,7 @@ package co.freeside.betamax.proxy import co.freeside.betamax.* import co.freeside.betamax.junit.* -import co.freeside.betamax.proxy.jetty.SimpleServer +import co.freeside.betamax.util.server.SimpleServer import co.freeside.betamax.util.httpbuilder.BetamaxRESTClient import co.freeside.betamax.util.server.SlowHandler import groovyx.net.http.HttpResponseException diff --git a/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/RequestMethodsSpec.groovy b/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/RequestMethodsSpec.groovy index 92180c8f..b0f7b65c 100644 --- a/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/RequestMethodsSpec.groovy +++ b/betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/RequestMethodsSpec.groovy @@ -18,9 +18,8 @@ package co.freeside.betamax.proxy import co.freeside.betamax.* import co.freeside.betamax.junit.* -import co.freeside.betamax.proxy.jetty.SimpleServer import co.freeside.betamax.util.httpbuilder.BetamaxRESTClient -import co.freeside.betamax.util.server.EchoHandler +import co.freeside.betamax.util.server.* import org.junit.Rule import spock.lang.* import static co.freeside.betamax.util.FileUtils.newTempDir @@ -34,14 +33,14 @@ class RequestMethodsSpec extends Specification { def recorder = new ProxyRecorder(tapeRoot: tapeRoot) @Rule RecorderRule recorderRule = new RecorderRule(recorder) - @Shared @AutoCleanup('stop') def endpoint = new SimpleServer() + @Shared @AutoCleanup('stop') def endpoint = new SimpleServer(EchoHandler) void setupSpec() { - endpoint.start(EchoHandler) + endpoint.start() } @Timeout(10) - @Betamax(tape = 'proxy network comms spec', mode = TapeMode.READ_WRITE) + @Betamax(tape = 'proxy network comms spec', mode = TapeMode.READ_WRITE) void 'proxy handles #method requests'() { given: def http = new BetamaxRESTClient(endpoint.url) diff --git a/betamax-test-support/betamax-test-support.gradle b/betamax-test-support/betamax-test-support.gradle index b1802f84..882e5e88 100644 --- a/betamax-test-support/betamax-test-support.gradle +++ b/betamax-test-support/betamax-test-support.gradle @@ -6,6 +6,6 @@ repositories { dependencies { compile project(":betamax-core") - compile project(":betamax-jetty") + compile commonDependencies.netty compile commonDependencies.httpBuilder } \ No newline at end of file diff --git a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/FileUtils.groovy b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/FileUtils.groovy deleted file mode 100644 index 1802c1a8..00000000 --- a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/FileUtils.groovy +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2011 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package co.freeside.betamax.util - -class FileUtils { - - private static final File TMP = new File(System.properties.'java.io.tmpdir') - - static File newTempDir(String name) { - def dir = new File(TMP, name) - dir.mkdirs() - dir - } - - private FileUtils() {} - -} diff --git a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/FileUtils.java b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/FileUtils.java new file mode 100644 index 00000000..ee314876 --- /dev/null +++ b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/FileUtils.java @@ -0,0 +1,18 @@ +package co.freeside.betamax.util; + +import java.io.*; + +public class FileUtils { + + private static final File TMP = new File(System.getProperties().getProperty("java.io.tmpdir")); + + @Deprecated + public static File newTempDir(String name) { + File dir = new File(TMP, name); + dir.mkdirs(); + return dir; + } + + private FileUtils() { + } +} diff --git a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/EchoHandler.groovy b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/EchoHandler.groovy index ef62a124..680a0811 100644 --- a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/EchoHandler.groovy +++ b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/EchoHandler.groovy @@ -17,60 +17,41 @@ package co.freeside.betamax.util.server import java.util.logging.Logger -import java.util.zip.* -import javax.servlet.http.* -import org.eclipse.jetty.server.Request -import org.eclipse.jetty.server.handler.AbstractHandler -import static org.eclipse.jetty.http.HttpHeaders.* -import static org.eclipse.jetty.http.HttpStatus.OK_200 +import io.netty.buffer.Unpooled +import io.netty.channel.* +import io.netty.handler.codec.http.* +import io.netty.util.CharsetUtil +import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE +import static io.netty.handler.codec.http.HttpResponseStatus.OK +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -class EchoHandler extends AbstractHandler { +@ChannelHandler.Sharable +class EchoHandler extends ChannelInboundHandlerAdapter { private static final log = Logger.getLogger(EchoHandler.name) - @Override - void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) { - log.fine "received $request.method request for $target" - response.status = OK_200 - response.contentType = 'text/plain' - - getResponseWriter(request, response).withWriter { writer -> - writer << request.method << ' ' << request.requestURI - if (request.queryString) { - writer << '?' << request.queryString - } - writer << ' ' << request.protocol << '\n' - for (headerName in request.headerNames) { - for (header in request.getHeaders(headerName)) { - writer << headerName << ': ' << header << '\n' - } - } - request.reader.withReader { reader -> - while (reader.ready()) { - writer << (char) reader.read() - } - } - } - } - - private Writer getResponseWriter(HttpServletRequest request, HttpServletResponse response) { - def out - def acceptedEncodings = request.getHeader(ACCEPT_ENCODING)?.tokenize(',') - log.fine "request accepts $acceptedEncodings" - if ('gzip' in acceptedEncodings) { - log.fine 'gzipping...' - response.addHeader(CONTENT_ENCODING, 'gzip') - out = new OutputStreamWriter(new GZIPOutputStream(response.outputStream)) - } else if ('deflate' in acceptedEncodings) { - log.fine 'deflating...' - response.addHeader(CONTENT_ENCODING, 'deflate') - out = new OutputStreamWriter(new DeflaterOutputStream(response.outputStream)) - } else { - log.fine 'not encoding...' - response.addHeader(CONTENT_ENCODING, 'none') - out = response.writer - } - out - } + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + FullHttpRequest request = (FullHttpRequest) msg; + FullHttpResponse response = new DefaultFullHttpResponse( + HTTP_1_1, + OK, + request.content() + ); + response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.printStackTrace(); + FullHttpResponse response = new DefaultFullHttpResponse( + HTTP_1_1, + OK, + Unpooled.copiedBuffer(cause.getClass().getSimpleName() + ": " + cause.getMessage(), CharsetUtil.UTF_8) + ); + response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + } } diff --git a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/HelloHandler.groovy b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/HelloHandler.groovy index 1e74db4d..b697e458 100644 --- a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/HelloHandler.groovy +++ b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/HelloHandler.groovy @@ -16,22 +16,40 @@ package co.freeside.betamax.util.server -import javax.servlet.http.* -import org.eclipse.jetty.server.Request -import org.eclipse.jetty.server.handler.AbstractHandler -import static org.eclipse.jetty.http.HttpStatus.OK_200 -import static org.eclipse.jetty.http.MimeTypes.TEXT_PLAIN +import io.netty.buffer.Unpooled +import io.netty.channel.* +import io.netty.handler.codec.http.* +import io.netty.util.CharsetUtil +import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE +import static io.netty.handler.codec.http.HttpResponseStatus.OK +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -class HelloHandler extends AbstractHandler { +@ChannelHandler.Sharable +class HelloHandler extends ChannelInboundHandlerAdapter { public static final String HELLO_WORLD = 'Hello World!' - @Override - void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) { - response.status = OK_200 - response.contentType = TEXT_PLAIN - response.outputStream.withWriter { writer -> - writer << HELLO_WORLD - } + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + FullHttpResponse response = new DefaultFullHttpResponse( + HTTP_1_1, + OK, + Unpooled.wrappedBuffer(HELLO_WORLD.bytes) + ); + response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.printStackTrace(); + FullHttpResponse response = new DefaultFullHttpResponse( + HTTP_1_1, + OK, + Unpooled.copiedBuffer(cause.getClass().getSimpleName() + ": " + cause.getMessage(), CharsetUtil.UTF_8) + ); + response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + } + } diff --git a/betamax-netty/src/main/java/co/freeside/betamax/proxy/netty/HttpChannelInitializer.java b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/HttpChannelInitializer.java similarity index 83% rename from betamax-netty/src/main/java/co/freeside/betamax/proxy/netty/HttpChannelInitializer.java rename to betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/HttpChannelInitializer.java index 3c26a842..8396403b 100644 --- a/betamax-netty/src/main/java/co/freeside/betamax/proxy/netty/HttpChannelInitializer.java +++ b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/HttpChannelInitializer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package co.freeside.betamax.proxy.netty; +package co.freeside.betamax.util.server; import io.netty.channel.*; import io.netty.channel.nio.*; @@ -45,19 +45,14 @@ public HttpChannelInitializer(int workerThreads, ChannelHandler handler) { @Override public void initChannel(SocketChannel channel) throws Exception { ChannelPipeline pipeline = channel.pipeline(); - -// SSLEngine engine = SslContextFactory.getServerContext().createSSLEngine(); -// engine.setUseClientMode(false); - -// pipeline.addLast("ssl", new SslHandler(engine)); pipeline.addLast("decoder", new HttpRequestDecoder()); pipeline.addLast("aggregator", new HttpObjectAggregator(MAX_CONTENT_LENGTH)); pipeline.addLast("encoder", new HttpResponseEncoder()); pipeline.addLast("chunkedWriter", new ChunkedWriteHandler()); if (workerGroup == null) { - pipeline.addLast("betamaxHandler", handler); + pipeline.addLast("handler", handler); } else { - pipeline.addLast(workerGroup, "betamaxHandler", handler); + pipeline.addLast(workerGroup, "handler", handler); } } } diff --git a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/HttpsChannelInitializer.java b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/HttpsChannelInitializer.java new file mode 100644 index 00000000..44f56ff7 --- /dev/null +++ b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/HttpsChannelInitializer.java @@ -0,0 +1,48 @@ +package co.freeside.betamax.util.server; + +import java.io.*; +import java.security.*; +import javax.net.ssl.*; +import io.netty.channel.*; +import io.netty.channel.socket.*; +import io.netty.handler.ssl.*; + +public class HttpsChannelInitializer extends HttpChannelInitializer { + + public HttpsChannelInitializer(int workerThreads, ChannelHandler handler) { + super(workerThreads, handler); + } + + @Override + public void initChannel(SocketChannel channel) throws Exception { + super.initChannel(channel); + + ChannelPipeline pipeline = channel.pipeline(); + + SSLEngine engine = sslContext().createSSLEngine(); + engine.setUseClientMode(false); + pipeline.addFirst("ssl", new SslHandler(engine)); + + } + + private static SSLContext sslContext() throws GeneralSecurityException, IOException { + InputStream keystoreStream = HttpsChannelInitializer.class.getResourceAsStream("/betamax.keystore"); + char[] password = "password".toCharArray(); + + SSLContext sslContext = SSLContext.getInstance("TLS"); + + KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(keystoreStream, password); + + String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm"); + if (algorithm == null) { + algorithm = "SunX509"; + } + KeyManagerFactory factory = KeyManagerFactory.getInstance(algorithm); + factory.init(keyStore, password); + + sslContext.init(factory.getKeyManagers(), null, null); + + return sslContext; + } +} diff --git a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/IncrementingHandler.groovy b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/IncrementingHandler.groovy index 2accc445..54f2772e 100644 --- a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/IncrementingHandler.groovy +++ b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/IncrementingHandler.groovy @@ -17,23 +17,40 @@ package co.freeside.betamax.util.server import java.util.concurrent.atomic.AtomicInteger -import javax.servlet.http.* -import org.eclipse.jetty.server.Request -import org.eclipse.jetty.server.handler.AbstractHandler -import static org.eclipse.jetty.http.HttpStatus.OK_200 -import static org.eclipse.jetty.http.MimeTypes.TEXT_PLAIN +import io.netty.buffer.Unpooled +import io.netty.channel.* +import io.netty.handler.codec.http.* +import io.netty.util.CharsetUtil +import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE +import static io.netty.handler.codec.http.HttpResponseStatus.OK +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1 -class IncrementingHandler extends AbstractHandler { +@ChannelHandler.Sharable +class IncrementingHandler extends ChannelInboundHandlerAdapter { private final AtomicInteger counter = new AtomicInteger() - @Override - void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) { - response.status = OK_200 - response.contentType = TEXT_PLAIN - response.outputStream.withWriter { writer -> - writer << 'count: ' << counter.incrementAndGet() - } - } + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + FullHttpResponse response = new DefaultFullHttpResponse( + HTTP_1_1, + OK, + Unpooled.wrappedBuffer("count: ${counter.incrementAndGet()}".bytes) + ); + response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.printStackTrace(); + FullHttpResponse response = new DefaultFullHttpResponse( + HTTP_1_1, + OK, + Unpooled.copiedBuffer(cause.getClass().getSimpleName() + ": " + cause.getMessage(), CharsetUtil.UTF_8) + ); + response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + } } diff --git a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/SimpleSecureServer.groovy b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/SimpleSecureServer.groovy index 5552c517..50b3ed67 100644 --- a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/SimpleSecureServer.groovy +++ b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/SimpleSecureServer.groovy @@ -16,34 +16,25 @@ package co.freeside.betamax.util.server -import co.freeside.betamax.proxy.jetty.SimpleServer import groovy.transform.InheritConstructors -import org.eclipse.jetty.server.* -import org.eclipse.jetty.server.ssl.SslSelectChannelConnector +import io.netty.channel.* @InheritConstructors class SimpleSecureServer extends SimpleServer { - @Override - String getUrl() { - "https://$host:$port/" - } + public static SimpleSecureServer start(Class handlerType) throws Exception { + SimpleSecureServer server = new SimpleSecureServer(handlerType); + server.start(); + return server; + } - @Override - protected Server createServer(int port) { - def server = super.createServer(port) + @Override + protected ChannelInitializer createChannelInitializer(ChannelHandler handler) { + return new HttpsChannelInitializer(0, handler) + } - def connector = new SslSelectChannelConnector() + protected String getUrlScheme() { + return "https"; + } - def keystore = SimpleSecureServer.getResource('/betamax.keystore') - - connector.port = port - connector.keystore = keystore - connector.password = 'password' - connector.keyPassword = 'password' - - server.connectors = [connector]as Connector[] - - server - } } diff --git a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/SimpleServer.java b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/SimpleServer.java new file mode 100644 index 00000000..0cfa0a30 --- /dev/null +++ b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/SimpleServer.java @@ -0,0 +1,80 @@ +package co.freeside.betamax.util.server; + +import java.net.*; +import io.netty.bootstrap.*; +import io.netty.channel.*; +import io.netty.channel.nio.*; +import io.netty.channel.socket.nio.*; + +public class SimpleServer { + + public static final int DEFAULT_PORT = 5000; + + private final int port; + private final ChannelInitializer channelInitializer; + private EventLoopGroup group; + private Channel channel; + + public SimpleServer(int port, ChannelHandler handler) { + this.port = port; + this.channelInitializer = createChannelInitializer(handler); + } + + public SimpleServer(ChannelHandler handler) { + this(DEFAULT_PORT, handler); + } + + public SimpleServer(int port, Class handlerType) throws IllegalAccessException, InstantiationException { + this(port, handlerType.newInstance()); + } + + public SimpleServer(Class handlerType) throws InstantiationException, IllegalAccessException { + this(DEFAULT_PORT, handlerType); + } + + public static SimpleServer start(Class handlerType) throws IllegalAccessException, InstantiationException, InterruptedException { + SimpleServer server = new SimpleServer(handlerType); + server.start(); + return server; + } + + public static SimpleServer start(ChannelHandler handler) throws InterruptedException { + SimpleServer server = new SimpleServer(handler); + server.start(); + return server; + } + + protected ChannelInitializer createChannelInitializer(ChannelHandler handler) { + return new HttpChannelInitializer(0, handler); + } + + protected String getUrlScheme() { + return "http"; + } + + public String getUrl() { + return String.format("%s://localhost:%s/", getUrlScheme(), port); + } + + public InetSocketAddress start() throws InterruptedException { + group = new NioEventLoopGroup(); + ServerBootstrap bootstrap = new ServerBootstrap(); + bootstrap.group(group) + .channel(NioServerSocketChannel.class) + .childHandler(channelInitializer) + .option(ChannelOption.SO_BACKLOG, 128) + .childOption(ChannelOption.SO_KEEPALIVE, true); + + channel = bootstrap.bind("localhost", port).sync().channel(); + return (InetSocketAddress) channel.localAddress(); + } + + public void stop() throws InterruptedException { + if (channel != null) { + channel.close().sync(); + } + if (group != null) { + group.shutdownGracefully().sync(); + } + } +} diff --git a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/SlowHandler.groovy b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/SlowHandler.groovy index c9c90825..de28bd24 100644 --- a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/SlowHandler.groovy +++ b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/server/SlowHandler.groovy @@ -16,35 +16,38 @@ package co.freeside.betamax.util.server -import java.util.concurrent.CountDownLatch import java.util.logging.Logger -import javax.servlet.http.* -import org.eclipse.jetty.server.Request -import org.eclipse.jetty.server.handler.AbstractHandler -import static java.util.concurrent.TimeUnit.SECONDS - +import io.netty.buffer.Unpooled +import io.netty.channel.* +import io.netty.handler.codec.http.* +import io.netty.util.CharsetUtil +import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE +import static io.netty.handler.codec.http.HttpResponseStatus.OK +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1 /** * A very dumb handler that will simply sit on any requests until it is told to shut down (i.e. the server is shutting * down). This is used for testing timeout conditions on clients. */ -class SlowHandler extends AbstractHandler { - - private static final log = Logger.getLogger(SlowHandler.name) - - private final CountDownLatch stopLatch = new CountDownLatch(1) - - @Override - void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) { - log.fine "received $request.method request for $target..." - stopLatch.await(30, SECONDS) - log.fine 'request complete...' - } - - @Override - protected void doStop() { - log.fine 'stopping handler...' - stopLatch.countDown() - super.doStop() - } +@ChannelHandler.Sharable +class SlowHandler extends ChannelInboundHandlerAdapter { + + private static final log = Logger.getLogger(SlowHandler.name) + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + log.fine "received ${((HttpRequest) msg).method} request..." + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.printStackTrace(); + FullHttpResponse response = new DefaultFullHttpResponse( + HTTP_1_1, + OK, + Unpooled.copiedBuffer(cause.getClass().getSimpleName() + ": " + cause.getMessage(), CharsetUtil.UTF_8) + ); + response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + } } diff --git a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/servlet/MockHttpServletResponse.groovy b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/servlet/MockHttpServletResponse.groovy deleted file mode 100644 index bc8e8a91..00000000 --- a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/servlet/MockHttpServletResponse.groovy +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright 2011 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package co.freeside.betamax.util.servlet - -import javax.servlet.ServletOutputStream -import javax.servlet.http.* -import org.apache.commons.collections.iterators.* - -class MockHttpServletResponse implements HttpServletResponse { - - int status - String message - String characterEncoding - String contentType - Locale locale - byte[] body - - private final Map> headers = [:] - private boolean written = false - private ServletOutputStream outputStream - private PrintWriter writer - - @Override - void sendError(int sc, String msg) { - status = sc - message = msg - } - - @Override - void sendError(int sc) { - status = sc - } - - @Override - void setStatus(int sc, String sm) { - status = sc - message = sm - } - - final String getHeader(String name) { - headers[name]?.join(', ') - } - - final Enumeration getHeaders(String name) { - def itr = headers.containsKey(name) ? headers[name].iterator() : EmptyIterator.INSTANCE - new IteratorEnumeration(itr) - } - - @Override - final boolean containsHeader(String name) { - headers.containsKey(name) - } - - @Override - final void setDateHeader(String name, long date) { - } - - @Override - final void addDateHeader(String name, long date) { - } - - @Override - final void setHeader(String name, String value) { - headers[name] = [value] - } - - @Override - final void addHeader(String name, String value) { - if (headers.containsKey(name)) { - headers[name] << value - } else { - setHeader(name, value) - } - } - - @Override - final void setIntHeader(String name, int value) { - setHeader(name, value.toString()) - } - - @Override - final void addIntHeader(String name, int value) { - addHeader(name, value.toString()) - } - - @Override - ServletOutputStream getOutputStream() { - if (!outputStream) { - if (written) { - throw new IllegalStateException() - } else { - written = true - outputStream = new MockServletOutputStream(this) - } - } - outputStream - } - - @Override - PrintWriter getWriter() { - if (!writer) { - if (written) { - throw new IllegalStateException() - } else { - written = true - writer = new PrintWriter(new MockServletOutputStream(this), characterEncoding) - } - } - writer - } - - @Override - void addCookie(Cookie cookie) { - throw new UnsupportedOperationException() - } - - @Override - String encodeURL(String url) { - throw new UnsupportedOperationException() - } - - @Override - String encodeRedirectURL(String url) { - throw new UnsupportedOperationException() - } - - @Override - String encodeUrl(String url) { - throw new UnsupportedOperationException() - } - - @Override - String encodeRedirectUrl(String url) { - throw new UnsupportedOperationException() - } - - @Override - void sendRedirect(String location) { - throw new UnsupportedOperationException() - } - - @Override - void setContentLength(int len) { - throw new UnsupportedOperationException() - } - - @Override - void setBufferSize(int size) { - throw new UnsupportedOperationException() - } - - @Override - int getBufferSize() { - throw new UnsupportedOperationException() - } - - @Override - void flushBuffer() { - } - - @Override - void resetBuffer() { - throw new UnsupportedOperationException() - } - - @Override - boolean isCommitted() { - throw new UnsupportedOperationException() - } - - @Override - void reset() { - throw new UnsupportedOperationException() - } - -} diff --git a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/servlet/MockServletInputStream.groovy b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/servlet/MockServletInputStream.groovy deleted file mode 100644 index d6bd870d..00000000 --- a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/servlet/MockServletInputStream.groovy +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2011 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package co.freeside.betamax.util.servlet - -import javax.servlet.ServletInputStream - -class MockServletInputStream extends ServletInputStream { - - private final InputStream delegate - - MockServletInputStream(InputStream delegate) { - this.delegate = delegate - } - - int read() { - delegate.read() - } - - @Override - void close() { - super.close() - delegate.close() - } - - @Override - synchronized void reset() { - super.reset() - delegate.reset() - } - -} diff --git a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/servlet/MockServletOutputStream.groovy b/betamax-test-support/src/main/groovy/co/freeside/betamax/util/servlet/MockServletOutputStream.groovy deleted file mode 100644 index dc0cf966..00000000 --- a/betamax-test-support/src/main/groovy/co/freeside/betamax/util/servlet/MockServletOutputStream.groovy +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2011 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package co.freeside.betamax.util.servlet - -import javax.servlet.ServletOutputStream - -class MockServletOutputStream extends ServletOutputStream { - - private final MockHttpServletResponse delegate - private final ByteArrayOutputStream stream = new ByteArrayOutputStream() - - MockServletOutputStream(MockHttpServletResponse delegate) { - this.delegate = delegate - } - - @Override - void write(int i) { - stream.write(i) - } - - @Override - void flush() { - super.flush() - stream.flush() - delegate.body = stream.toByteArray() - } - - @Override - void close() { - super.close() - stream.close() - delegate.body = stream.toByteArray() - } - -} diff --git a/betamax-netty/src/test/groovy/co/freeside/betamax/proxy/netty/NettyServerSpec.groovy b/betamax-test-support/src/test/groovy/co/freeside/betamax/proxy/netty/NettyServerSpec.groovy similarity index 86% rename from betamax-netty/src/test/groovy/co/freeside/betamax/proxy/netty/NettyServerSpec.groovy rename to betamax-test-support/src/test/groovy/co/freeside/betamax/proxy/netty/NettyServerSpec.groovy index 27b6b6fd..4f14f592 100644 --- a/betamax-netty/src/test/groovy/co/freeside/betamax/proxy/netty/NettyServerSpec.groovy +++ b/betamax-test-support/src/test/groovy/co/freeside/betamax/proxy/netty/NettyServerSpec.groovy @@ -16,15 +16,15 @@ package co.freeside.betamax.proxy.netty +import co.freeside.betamax.util.server.* import spock.lang.Specification class NettyServerSpec extends Specification { void "can serve HTTP responses with Netty"() { given: - def channelInitializer = new HttpChannelInitializer(0, new EchoServerHandler()) - def server = new NettyBetamaxServer(port, channelInitializer) - server.run() + def server = new SimpleServer(port, EchoHandler) + server.start() when: HttpURLConnection connection = new URL("http://localhost:$port/").openConnection() @@ -40,7 +40,7 @@ class NettyServerSpec extends Specification { connection.inputStream.getText("UTF-8") == message cleanup: - server.shutdown() + server.stop() where: port = 5000 diff --git a/settings.gradle b/settings.gradle index 5afd4019..f0af953a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,7 +3,6 @@ include "betamax-core", "betamax-jetty", "betamax-proxy", "betamax-manual", - "betamax-netty", "betamax-test-support" rootProject.name = "betamax"