package netflix.karyon.transport.http; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.TypeLiteral; import com.netflix.governator.guice.LifecycleInjector; import com.netflix.governator.lifecycle.LifecycleManager; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpResponseStatus; import io.reactivex.netty.RxNetty; import io.reactivex.netty.protocol.http.client.HttpClientResponse; import io.reactivex.netty.protocol.http.server.HttpServerRequest; import io.reactivex.netty.protocol.http.server.HttpServerResponse; import io.reactivex.netty.protocol.http.server.RequestHandler; import io.reactivex.netty.server.RxServer; import netflix.karyon.transport.interceptor.DuplexInterceptor; import org.junit.After; import org.junit.Before; import org.junit.Test; import rx.Observable; import rx.functions.Func1; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.assertEquals; /** * @author Tomasz Bak */ public class KaryonHttpModuleTest { private static final Key<Map<String, RxServer>> RX_SERVERS_KEY = Key.get(new TypeLiteral<Map<String, RxServer>>() { }); private Injector injector; private LifecycleManager lifecycleManager; private RxServer server; @Before public void setUp() throws Exception { injector = LifecycleInjector.bootstrap(TestableHttpModule.class); lifecycleManager = injector.getInstance(LifecycleManager.class); lifecycleManager.start(); server = injector.getInstance(RX_SERVERS_KEY).values().iterator().next(); } @After public void tearDown() throws Exception { if (lifecycleManager != null) { lifecycleManager.close(); } } @Test public void testHttpRouterAndInterceptorSupport() throws Exception { int counterInitial = CountingInterceptor.counter.get(); HttpResponseStatus status = sendRequest("/sendOK", server); assertEquals("Expected HTTP 200", HttpResponseStatus.OK, status); status = sendRequest("/sendNotFound", server); assertEquals("Expected HTTP NOT_FOUND", HttpResponseStatus.NOT_FOUND, status); int counterFinal = CountingInterceptor.counter.get(); assertEquals("Invalid number of counting interceptor invocations", 4, counterFinal - counterInitial); } private HttpResponseStatus sendRequest(String path, RxServer server) throws Exception { return (HttpResponseStatus) RxNetty.createHttpGet("http://localhost:" + server.getServerPort() + path) .flatMap(new Func1<HttpClientResponse<ByteBuf>, Observable<?>>() { @Override public Observable<HttpResponseStatus> call(HttpClientResponse<ByteBuf> httpClientResponse) { return Observable.just(httpClientResponse.getStatus()); } }).single().toBlocking().toFuture().get(60, TimeUnit.SECONDS); } public static class TestableRequestRouter implements RequestHandler<ByteBuf, ByteBuf> { @Override public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { if (request.getPath().contains("/sendOK")) { response.setStatus(HttpResponseStatus.OK); } else if (request.getPath().contains("/sendNotFound")) { response.setStatus(HttpResponseStatus.NOT_FOUND); } else { response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR); } return Observable.empty(); } } public static class TestableHttpModule extends KaryonHttpModule<ByteBuf, ByteBuf> { public TestableHttpModule() { super("testableHttpModule", ByteBuf.class, ByteBuf.class); } @Override protected void configureServer() { bindRouter().to(TestableRequestRouter.class); interceptorSupport().forHttpMethod(HttpMethod.GET).intercept(CountingInterceptor.class); server().port(0); } } public static class CountingInterceptor implements DuplexInterceptor<HttpServerRequest<ByteBuf>, HttpServerResponse<ByteBuf>> { static AtomicInteger counter = new AtomicInteger(); @Override public Observable<Void> in(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { counter.incrementAndGet(); return Observable.empty(); } @Override public Observable<Void> out(HttpServerResponse<ByteBuf> response) { counter.incrementAndGet(); return Observable.empty(); } } }