From b2135d4c80e366343f0b265d808df82fb6e619ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=CC=88rg=20Prante?= Date: Wed, 13 Oct 2021 18:20:36 +0200 Subject: [PATCH] update to 4.1.69, remove rx/reactive subprojects --- gradle.properties | 10 +- netty-http-rx/NOTICE.txt | 5 - netty-http-rx/build.gradle | 8 - .../java/io/reactivex/netty/HandlerNames.java | 46 - .../main/java/io/reactivex/netty/RxNetty.java | 97 -- .../AbstractConnectionToChannelBridge.java | 392 -------- .../netty/channel/AllocatingTransformer.java | 72 -- .../netty/channel/AppendTransformerEvent.java | 40 - .../netty/channel/AutoReleaseOperator.java | 49 - .../channel/BackpressureManagingHandler.java | 710 ------------- .../netty/channel/BytesInspector.java | 99 -- .../netty/channel/ChannelOperations.java | 277 ------ .../netty/channel/ChannelSubscriberEvent.java | 45 - .../reactivex/netty/channel/Connection.java | 314 ------ .../ConnectionCreationFailedEvent.java | 45 - .../netty/channel/ConnectionImpl.java | 240 ----- .../ConnectionInputSubscriberEvent.java | 64 -- ...ConnectionInputSubscriberReplaceEvent.java | 33 - .../ConnectionInputSubscriberResetEvent.java | 26 - .../channel/ConnectionSubscriberEvent.java | 44 - .../netty/channel/ContentSource.java | 96 -- .../channel/DefaultChannelOperations.java | 368 ------- .../channel/DetachedChannelPipeline.java | 387 ------- .../channel/DisposableContentSource.java | 134 --- .../netty/channel/EmitConnectionEvent.java | 40 - .../netty/channel/FlushSelectorOperator.java | 57 -- .../netty/channel/MarkAwarePipeline.java | 471 --------- .../SubscriberToChannelFutureBridge.java | 70 -- .../netty/channel/WriteTransformations.java | 98 -- .../netty/channel/WriteTransformer.java | 78 -- .../events/ConnectionEventListener.java | 124 --- .../events/ConnectionEventPublisher.java | 238 ----- .../netty/client/ChannelProvider.java | 27 - .../netty/client/ChannelProviderFactory.java | 29 - .../ClientConnectionToChannelBridge.java | 202 ---- .../reactivex/netty/client/ClientState.java | 505 ---------- .../netty/client/ConnectionProvider.java | 38 - .../client/ConnectionProviderFactory.java | 26 - .../netty/client/ConnectionRequest.java | 42 - .../java/io/reactivex/netty/client/Host.java | 70 -- .../reactivex/netty/client/HostConnector.java | 110 -- .../client/events/ClientEventListener.java | 111 --- .../SingleHostConnectionProvider.java | 51 - .../loadbalancer/AbstractP2CStrategy.java | 113 --- .../client/loadbalancer/HostCollector.java | 50 - .../netty/client/loadbalancer/HostHolder.java | 65 -- .../loadbalancer/LoadBalancerFactory.java | 94 -- .../loadbalancer/LoadBalancingStrategy.java | 31 - .../loadbalancer/NoBufferHostCollector.java | 74 -- .../NoHostsAvailableException.java | 55 - ...mpositePoolLimitDeterminationStrategy.java | 75 -- .../pool/FIFOIdleConnectionsHolder.java | 75 -- .../client/pool/IdleConnectionsHolder.java | 72 -- .../pool/MaxConnectionsBasedStrategy.java | 107 -- .../netty/client/pool/PoolConfig.java | 93 -- .../client/pool/PoolExhaustedException.java | 37 - .../pool/PoolLimitDeterminationStrategy.java | 48 - .../netty/client/pool/PooledConnection.java | 373 ------- .../client/pool/PooledConnectionProvider.java | 63 -- .../pool/PooledConnectionProviderImpl.java | 431 -------- .../pool/PreferCurrentEventLoopHolder.java | 168 ---- .../SingleHostPoolingProviderFactory.java | 60 -- ...boundedPoolLimitDeterminationStrategy.java | 41 - .../java/io/reactivex/netty/events/Clock.java | 140 --- .../netty/events/EventAttributeKeys.java | 39 - .../reactivex/netty/events/EventListener.java | 105 -- .../netty/events/EventPublisher.java | 32 - .../reactivex/netty/events/EventSource.java | 34 - .../events/ListenerInvocationException.java | 65 -- .../netty/events/ListenersHolder.java | 406 -------- .../events/internal/SafeEventListener.java | 26 - .../internal/ExecuteInEventloopAction.java | 39 - .../internal/InternalReadTimeoutHandler.java | 247 ----- .../netty/internal/VoidToAnythingCast.java | 33 - .../netty/protocol/http/CookiesHolder.java | 95 -- .../netty/protocol/http/HttpHandlerNames.java | 51 - .../netty/protocol/http/TrailingHeaders.java | 116 --- .../protocol/http/client/HttpClient.java | 363 ------- .../protocol/http/client/HttpClientImpl.java | 312 ------ .../client/HttpClientInterceptorChain.java | 82 -- .../HttpClientInterceptorChainImpl.java | 56 -- .../http/client/HttpClientRequest.java | 575 ----------- .../http/client/HttpClientResponse.java | 410 -------- .../http/client/HttpRedirectException.java | 64 -- .../http/client/InterceptingHttpClient.java | 136 --- .../client/InterceptingHttpClientImpl.java | 100 -- .../protocol/http/client/Interceptor.java | 37 - .../protocol/http/client/RequestProvider.java | 41 - .../http/client/TransformingInterceptor.java | 37 - .../events/HttpClientEventPublisher.java | 343 ------- .../events/HttpClientEventsListener.java | 99 -- .../events/SafeHttpClientEventsListener.java | 306 ------ .../client/internal/HttpChannelProvider.java | 54 - .../internal/HttpChannelProviderFactory.java | 55 - .../internal/HttpClientRequestImpl.java | 542 ---------- .../internal/HttpClientResponseImpl.java | 353 ------- .../HttpClientToConnectionBridge.java | 193 ---- .../http/client/internal/RawRequest.java | 244 ----- .../http/client/internal/Redirector.java | 201 ---- .../client/internal/UnusableConnection.java | 194 ---- .../loadbalancer/EWMABasedP2CStrategy.java | 111 --- .../AbstractHttpConnectionBridge.java | 555 ----------- .../internal/HttpContentSubscriberEvent.java | 32 - .../http/internal/HttpMessageFormatter.java | 72 -- .../http/internal/OperatorTrailer.java | 77 -- .../http/internal/UnsafeEmptySubscriber.java | 52 - .../http/server/ContentWriterImpl.java | 218 ---- .../http/server/FailedContentWriter.java | 126 --- .../http/server/HttpConnectionHandler.java | 207 ---- .../protocol/http/server/HttpServer.java | 445 --------- .../protocol/http/server/HttpServerImpl.java | 228 ----- .../server/HttpServerInterceptorChain.java | 245 ----- .../http/server/HttpServerRequest.java | 441 -------- .../http/server/HttpServerRequestImpl.java | 307 ------ .../http/server/HttpServerResponse.java | 385 ------- .../http/server/HttpServerResponseImpl.java | 432 -------- .../server/HttpServerToConnectionBridge.java | 180 ---- .../protocol/http/server/RequestHandler.java | 39 - .../http/server/ResponseContentWriter.java | 310 ------ .../protocol/http/server/UriInfoHolder.java | 79 -- .../events/HttpServerEventPublisher.java | 304 ------ .../events/HttpServerEventsListener.java | 107 -- .../events/SafeHttpServerEventsListener.java | 263 ----- .../protocol/http/sse/ServerSentEvent.java | 329 ------ .../sse/client/ServerSentEventDecoder.java | 355 ------- .../sse/server/ServerSentEventEncoder.java | 123 --- .../util/HttpContentStringLineDecoder.java | 53 - .../protocol/http/ws/WebSocketConnection.java | 159 --- ...peratorCacheSingleWebsocketConnection.java | 142 --- .../http/ws/client/WebSocketRequest.java | 51 - .../http/ws/client/WebSocketResponse.java | 43 - .../http/ws/client/Ws7To13UpgradeHandler.java | 149 --- .../client/internal/WebSocketRequestImpl.java | 127 --- .../internal/WebSocketResponseImpl.java | 268 ----- .../protocol/http/ws/internal/WsUtils.java | 93 -- .../http/ws/server/V7to13Handshaker.java | 162 --- .../http/ws/server/WebSocketHandler.java | 36 - .../http/ws/server/WebSocketHandlers.java | 54 - .../http/ws/server/WebSocketHandshaker.java | 152 --- .../http/ws/server/Ws7To13UpgradeHandler.java | 198 ---- .../netty/protocol/tcp/TcpHandlerNames.java | 41 - .../tcp/client/ConnectionRequestImpl.java | 34 - .../tcp/client/InterceptingTcpClient.java | 41 - .../tcp/client/InterceptingTcpClientImpl.java | 53 - .../protocol/tcp/client/Interceptor.java | 39 - .../netty/protocol/tcp/client/TcpClient.java | 361 ------- .../protocol/tcp/client/TcpClientImpl.java | 361 ------- .../tcp/client/TcpClientInterceptorChain.java | 82 -- .../client/TcpClientInterceptorChainImpl.java | 58 -- .../tcp/client/TransformingInterceptor.java | 41 - .../events/SafeTcpClientEventListener.java | 237 ----- .../client/events/TcpClientEventListener.java | 27 - .../events/TcpClientEventPublisher.java | 280 ------ .../client/internal/TcpChannelProvider.java | 58 -- .../internal/TcpChannelProviderFactory.java | 45 - .../tcp/server/ConnectionHandler.java | 38 - .../netty/protocol/tcp/server/TcpServer.java | 442 -------- .../TcpServerConnectionToChannelBridge.java | 157 --- .../protocol/tcp/server/TcpServerImpl.java | 281 ------ .../tcp/server/TcpServerInterceptorChain.java | 257 ----- .../protocol/tcp/server/TcpServerState.java | 155 --- .../events/SafeTcpServerEventListener.java | 187 ---- .../server/events/TcpServerEventListener.java | 60 -- .../events/TcpServerEventPublisher.java | 199 ---- .../reactivex/netty/server/ServerState.java | 194 ---- .../reactivex/netty/ssl/DefaultSslCodec.java | 66 -- .../java/io/reactivex/netty/ssl/SslCodec.java | 91 -- .../threads/PreferCurrentEventLoopGroup.java | 215 ---- .../netty/threads/RxDefaultThreadFactory.java | 26 - .../netty/threads/RxEventLoopProvider.java | 83 -- .../threads/RxJavaEventloopScheduler.java | 130 --- .../RxJavaNettyBasedSchedulersHook.java | 50 - .../netty/threads/SingleNioLoopProvider.java | 147 --- .../io/reactivex/netty/util/CollectBytes.java | 109 -- .../io/reactivex/netty/util/LineReader.java | 135 --- .../netty/util/LoggingHandlerFactory.java | 72 -- .../netty/util/StringLineDecoder.java | 42 - .../netty/util/UnicastBufferingSubject.java | 286 ------ ...AbstractConnectionToChannelBridgeTest.java | 310 ------ .../BackpressureManagingHandlerTest.java | 458 --------- .../channel/BytesWriteInterceptorTest.java | 425 -------- .../netty/channel/ConnectionImplTest.java | 199 ---- .../netty/channel/ContentSourceRule.java | 100 -- .../netty/channel/ContentSourceTest.java | 69 -- .../channel/DefaultChannelOperationsTest.java | 393 -------- .../channel/DetachedChannelPipelineTest.java | 234 ----- .../netty/channel/ReadProducerTest.java | 149 --- .../SubscriberToChannelFutureBridgeTest.java | 111 --- .../channel/WriteStreamSubscriberTest.java | 285 ------ .../netty/channel/WriteTransformerTest.java | 149 --- .../events/ConnectionEventPublisherTest.java | 196 ---- .../netty/client/ClientStateTest.java | 306 ------ .../reactivex/netty/client/SslClientTest.java | 68 -- .../loadbalancer/AbstractP2CStrategyTest.java | 181 ---- .../loadbalancer/LoadBalancerFactoryTest.java | 196 ---- .../pool/FIFOIdleConnectionsHolderTest.java | 159 --- .../client/pool/PoolLimitStrategyTest.java | 119 --- .../PooledConnectionProviderImplTest.java | 382 ------- .../PreferCurrentEventLoopHolderTest.java | 226 ----- .../netty/events/ListenersHolderRule.java | 81 -- .../netty/events/ListenersHolderTest.java | 564 ----------- .../protocol/http/CookiesHolderTest.java | 88 -- .../http/client/EventListenerTest.java | 143 --- .../http/client/HttpClientPoolTest.java | 186 ---- .../protocol/http/client/HttpClientRule.java | 314 ------ .../protocol/http/client/HttpClientTest.java | 259 ----- .../http/client/HttpRedirectTest.java | 274 ----- .../http/client/RedirectOperatorTest.java | 166 ---- .../events/HttpClientEventPublisherTest.java | 309 ------ .../events/HttpClientEventsListenerImpl.java | 274 ----- .../client/events/HttpClientEventsTest.java | 66 -- .../internal/HttpClientRequestImplTest.java | 940 ------------------ .../AbstractHttpConnectionBridgeTest.java | 522 ---------- .../protocol/http/server/CookieTest.java | 84 -- .../protocol/http/server/Http10Test.java | 70 -- .../http/server/HttpEndToEndTest.java | 63 -- .../http/server/HttpServerRequestUriTest.java | 87 -- .../protocol/http/server/HttpServerRule.java | 237 ----- .../protocol/http/server/HttpServerTest.java | 108 -- .../HttpServerToConnectionBridgeTest.java | 45 - .../protocol/http/server/PipeliningTest.java | 92 -- .../events/HttpServerEventPublisherTest.java | 284 ------ .../events/HttpServerEventsListenerImpl.java | 233 ----- .../http/sse/ServerSentEventEndToEndTest.java | 128 --- .../netty/protocol/http/sse/SseTestUtil.java | 74 -- .../client/ServerSentEventDecoderTest.java | 259 ----- .../server/ServerSentEventEncoderTest.java | 139 --- ...torCacheSingleWebsocketConnectionTest.java | 230 ----- .../WSEagerInputSubscriptionHandlerTest.java | 77 -- .../tcp/client/EventListenerTest.java | 116 --- .../client/MockTcpClientEventListener.java | 192 ---- .../client/PoolingWithRealChannelTest.java | 166 ---- .../tcp/client/TcpClientImplTest.java | 217 ---- .../protocol/tcp/client/TcpClientRule.java | 156 --- .../events/TcpClientEventPublisherTest.java | 267 ----- .../client/events/TcpClientEventsTest.java | 51 - ...UnexpectedConnectionHandlerErrorsTest.java | 118 --- .../events/TcpServerEventPublisherTest.java | 204 ---- .../netty/test/util/FlushSelector.java | 38 - .../netty/test/util/InboundRequestFeeder.java | 105 -- .../test/util/MockClientEventListener.java | 235 ----- .../util/MockConnectionEventListener.java | 200 ---- .../netty/test/util/MockEventListener.java | 164 --- .../netty/test/util/MockEventPublisher.java | 58 -- .../MockPoolLimitDeterminationStrategy.java | 59 -- .../netty/test/util/MockProducer.java | 58 -- .../test/util/MockTcpServerEventListener.java | 169 ---- .../test/util/TcpConnectionRequestMock.java | 34 - .../util/TrackableMetricEventsListener.java | 127 --- .../EmbeddedChannelPipelineDelegate.java | 438 -------- .../embedded/EmbeddedChannelProvider.java | 103 -- .../embedded/EmbeddedChannelWithFeeder.java | 53 - .../embedded/EmbeddedConnectionProvider.java | 64 -- .../PreferCurrentEventLoopGroupTest.java | 45 - .../threads/RxJavaEventloopSchedulerTest.java | 152 --- .../netty/util/CollectBytesTest.java | 211 ---- .../reactivex/netty/util/LineReaderTest.java | 127 --- .../util/UnicastBufferingSubjectTest.java | 195 ---- .../src/test/resources/log4j.properties | 21 - netty-http-server-reactive/NOTICE.txt | 3 - netty-http-server-reactive/build.gradle | 7 - .../server/reactive/CancelledSubscriber.java | 34 - .../server/reactive/DelegateHttpMessage.java | 58 -- .../server/reactive/DelegateHttpRequest.java | 55 - .../server/reactive/DelegateHttpResponse.java | 38 - .../reactive/DelegateStreamedHttpRequest.java | 21 - .../DelegateStreamedHttpResponse.java | 21 - .../server/reactive/EmptyHttpRequest.java | 135 --- .../server/reactive/EmptyHttpResponse.java | 129 --- .../server/reactive/HandlerPublisher.java | 468 --------- .../server/reactive/HandlerSubscriber.java | 266 ----- .../reactive/HttpStreamsClientHandler.java | 152 --- .../server/reactive/HttpStreamsHandler.java | 321 ------ .../reactive/HttpStreamsServerHandler.java | 217 ---- .../server/reactive/StreamedHttpMessage.java | 16 - .../server/reactive/StreamedHttpRequest.java | 11 - .../server/reactive/StreamedHttpResponse.java | 11 - .../reactive/WebSocketHttpResponse.java | 24 - .../server/reactive/test/BatchedProducer.java | 57 -- .../reactive/test/ChannelPublisherTest.java | 157 --- .../reactive/test/ClosedLoopChannel.java | 108 -- settings.gradle | 2 - 282 files changed, 3 insertions(+), 44789 deletions(-) delete mode 100644 netty-http-rx/NOTICE.txt delete mode 100644 netty-http-rx/build.gradle delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/HandlerNames.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/RxNetty.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/AbstractConnectionToChannelBridge.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/AllocatingTransformer.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/AppendTransformerEvent.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/AutoReleaseOperator.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/BackpressureManagingHandler.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/BytesInspector.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/ChannelOperations.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/ChannelSubscriberEvent.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/Connection.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionCreationFailedEvent.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionInputSubscriberEvent.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionInputSubscriberReplaceEvent.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionInputSubscriberResetEvent.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionSubscriberEvent.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/ContentSource.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/DefaultChannelOperations.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/DetachedChannelPipeline.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/DisposableContentSource.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/EmitConnectionEvent.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/FlushSelectorOperator.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/MarkAwarePipeline.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/SubscriberToChannelFutureBridge.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/WriteTransformations.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/WriteTransformer.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/events/ConnectionEventListener.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/channel/events/ConnectionEventPublisher.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/ChannelProvider.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/ChannelProviderFactory.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/ClientConnectionToChannelBridge.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/ClientState.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/ConnectionProvider.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/ConnectionProviderFactory.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/ConnectionRequest.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/Host.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/HostConnector.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/events/ClientEventListener.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/internal/SingleHostConnectionProvider.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/AbstractP2CStrategy.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/HostCollector.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/HostHolder.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/LoadBalancerFactory.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/LoadBalancingStrategy.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/NoBufferHostCollector.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/NoHostsAvailableException.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/pool/CompositePoolLimitDeterminationStrategy.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/pool/FIFOIdleConnectionsHolder.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/pool/IdleConnectionsHolder.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/pool/MaxConnectionsBasedStrategy.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PoolConfig.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PoolExhaustedException.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PoolLimitDeterminationStrategy.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PooledConnection.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PooledConnectionProvider.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PooledConnectionProviderImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PreferCurrentEventLoopHolder.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/pool/SingleHostPoolingProviderFactory.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/client/pool/UnboundedPoolLimitDeterminationStrategy.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/events/Clock.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/events/EventAttributeKeys.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/events/EventListener.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/events/EventPublisher.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/events/EventSource.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/events/ListenerInvocationException.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/events/ListenersHolder.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/events/internal/SafeEventListener.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/internal/ExecuteInEventloopAction.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/internal/InternalReadTimeoutHandler.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/internal/VoidToAnythingCast.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/CookiesHolder.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/HttpHandlerNames.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/TrailingHeaders.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClient.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientInterceptorChain.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientInterceptorChainImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientRequest.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientResponse.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpRedirectException.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/InterceptingHttpClient.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/InterceptingHttpClientImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/Interceptor.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/RequestProvider.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/TransformingInterceptor.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventPublisher.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventsListener.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/events/SafeHttpClientEventsListener.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpChannelProvider.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpChannelProviderFactory.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpClientRequestImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpClientResponseImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpClientToConnectionBridge.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/RawRequest.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/Redirector.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/UnusableConnection.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/loadbalancer/EWMABasedP2CStrategy.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/AbstractHttpConnectionBridge.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/HttpContentSubscriberEvent.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/HttpMessageFormatter.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/OperatorTrailer.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/UnsafeEmptySubscriber.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/ContentWriterImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/FailedContentWriter.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpConnectionHandler.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServer.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerInterceptorChain.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerRequest.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerRequestImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerResponse.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerResponseImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerToConnectionBridge.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/RequestHandler.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/ResponseContentWriter.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/UriInfoHolder.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/events/HttpServerEventPublisher.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/events/HttpServerEventsListener.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/events/SafeHttpServerEventsListener.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/sse/ServerSentEvent.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/sse/client/ServerSentEventDecoder.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/sse/server/ServerSentEventEncoder.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/util/HttpContentStringLineDecoder.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/WebSocketConnection.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/OperatorCacheSingleWebsocketConnection.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/WebSocketRequest.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/WebSocketResponse.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/Ws7To13UpgradeHandler.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/internal/WebSocketRequestImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/internal/WebSocketResponseImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/internal/WsUtils.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/V7to13Handshaker.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/WebSocketHandler.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/WebSocketHandlers.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/WebSocketHandshaker.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/Ws7To13UpgradeHandler.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/TcpHandlerNames.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/ConnectionRequestImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/InterceptingTcpClient.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/InterceptingTcpClientImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/Interceptor.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TcpClient.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TcpClientImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TcpClientInterceptorChain.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TcpClientInterceptorChainImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TransformingInterceptor.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/events/SafeTcpClientEventListener.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/events/TcpClientEventListener.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/events/TcpClientEventPublisher.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/internal/TcpChannelProvider.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/internal/TcpChannelProviderFactory.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/ConnectionHandler.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServer.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServerConnectionToChannelBridge.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServerImpl.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServerInterceptorChain.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServerState.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/events/SafeTcpServerEventListener.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/events/TcpServerEventListener.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/events/TcpServerEventPublisher.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/server/ServerState.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/ssl/DefaultSslCodec.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/ssl/SslCodec.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/threads/PreferCurrentEventLoopGroup.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/threads/RxDefaultThreadFactory.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/threads/RxEventLoopProvider.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/threads/RxJavaEventloopScheduler.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/threads/RxJavaNettyBasedSchedulersHook.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/threads/SingleNioLoopProvider.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/util/CollectBytes.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/util/LineReader.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/util/LoggingHandlerFactory.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/util/StringLineDecoder.java delete mode 100644 netty-http-rx/src/main/java/io/reactivex/netty/util/UnicastBufferingSubject.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/channel/AbstractConnectionToChannelBridgeTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/channel/BackpressureManagingHandlerTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/channel/BytesWriteInterceptorTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/channel/ConnectionImplTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/channel/ContentSourceRule.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/channel/ContentSourceTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/channel/DefaultChannelOperationsTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/channel/DetachedChannelPipelineTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/channel/ReadProducerTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/channel/SubscriberToChannelFutureBridgeTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/channel/WriteStreamSubscriberTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/channel/WriteTransformerTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/channel/events/ConnectionEventPublisherTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/client/ClientStateTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/client/SslClientTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/client/loadbalancer/AbstractP2CStrategyTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/client/loadbalancer/LoadBalancerFactoryTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/client/pool/FIFOIdleConnectionsHolderTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/client/pool/PoolLimitStrategyTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/client/pool/PooledConnectionProviderImplTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/client/pool/PreferCurrentEventLoopHolderTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/events/ListenersHolderRule.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/events/ListenersHolderTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/CookiesHolderTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/EventListenerTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/HttpClientPoolTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/HttpClientRule.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/HttpClientTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/HttpRedirectTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/RedirectOperatorTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventPublisherTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventsListenerImpl.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventsTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/internal/HttpClientRequestImplTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/internal/AbstractHttpConnectionBridgeTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/CookieTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/Http10Test.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpEndToEndTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpServerRequestUriTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpServerRule.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpServerTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpServerToConnectionBridgeTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/PipeliningTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/events/HttpServerEventPublisherTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/events/HttpServerEventsListenerImpl.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/sse/ServerSentEventEndToEndTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/sse/SseTestUtil.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/sse/client/ServerSentEventDecoderTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/sse/server/ServerSentEventEncoderTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/ws/client/OperatorCacheSingleWebsocketConnectionTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/ws/server/WSEagerInputSubscriptionHandlerTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/EventListenerTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/MockTcpClientEventListener.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/PoolingWithRealChannelTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/TcpClientImplTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/TcpClientRule.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/events/TcpClientEventPublisherTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/events/TcpClientEventsTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/server/UnexpectedConnectionHandlerErrorsTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/server/events/TcpServerEventPublisherTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/test/util/FlushSelector.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/test/util/InboundRequestFeeder.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockClientEventListener.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockConnectionEventListener.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockEventListener.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockEventPublisher.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockPoolLimitDeterminationStrategy.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockProducer.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockTcpServerEventListener.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/test/util/TcpConnectionRequestMock.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/test/util/TrackableMetricEventsListener.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/test/util/embedded/EmbeddedChannelPipelineDelegate.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/test/util/embedded/EmbeddedChannelProvider.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/test/util/embedded/EmbeddedChannelWithFeeder.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/test/util/embedded/EmbeddedConnectionProvider.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/threads/PreferCurrentEventLoopGroupTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/threads/RxJavaEventloopSchedulerTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/util/CollectBytesTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/util/LineReaderTest.java delete mode 100644 netty-http-rx/src/test/java/io/reactivex/netty/util/UnicastBufferingSubjectTest.java delete mode 100644 netty-http-rx/src/test/resources/log4j.properties delete mode 100644 netty-http-server-reactive/NOTICE.txt delete mode 100644 netty-http-server-reactive/build.gradle delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/CancelledSubscriber.java delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateHttpMessage.java delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateHttpRequest.java delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateHttpResponse.java delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateStreamedHttpRequest.java delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateStreamedHttpResponse.java delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/EmptyHttpRequest.java delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/EmptyHttpResponse.java delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HandlerPublisher.java delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HandlerSubscriber.java delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HttpStreamsClientHandler.java delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HttpStreamsHandler.java delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HttpStreamsServerHandler.java delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/StreamedHttpMessage.java delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/StreamedHttpRequest.java delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/StreamedHttpResponse.java delete mode 100644 netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/WebSocketHttpResponse.java delete mode 100644 netty-http-server-reactive/src/test/java/org/xbib/netty/http/server/reactive/test/BatchedProducer.java delete mode 100644 netty-http-server-reactive/src/test/java/org/xbib/netty/http/server/reactive/test/ChannelPublisherTest.java delete mode 100644 netty-http-server-reactive/src/test/java/org/xbib/netty/http/server/reactive/test/ClosedLoopChannel.java diff --git a/gradle.properties b/gradle.properties index c582403..f36e95b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,14 +1,12 @@ group = org.xbib name = netty-http -version = 4.1.68.0 +version = 4.1.69.0 gradle.wrapper.version = 6.6.1 -netty.version = 4.1.68.Final -tcnative.version = 2.0.43.Final +netty.version = 4.1.69.Final +tcnative.version = 2.0.44.Final bouncycastle.version = 1.69 -reactivestreams.version = 1.0.3 -reactivex.version = 1.3.8 conscrypt.version = 2.5.2 javassist.version = 3.28.0-GA jackson.version = 2.11.4 @@ -16,5 +14,3 @@ mockito.version = 3.10.0 xbib.net.version = 2.1.1 xbib-guice.version = 4.4.2 junit.version = 5.7.1 -# uuhh, too many tests to update to jupiter in rx... -junit4.version = 4.13.1 diff --git a/netty-http-rx/NOTICE.txt b/netty-http-rx/NOTICE.txt deleted file mode 100644 index 3241e88..0000000 --- a/netty-http-rx/NOTICE.txt +++ /dev/null @@ -1,5 +0,0 @@ -This work is based on - -https://github.com/ReactiveX/RxNetty - -(branch 0.5.x as of 22-Sep-2019) diff --git a/netty-http-rx/build.gradle b/netty-http-rx/build.gradle deleted file mode 100644 index df6cb51..0000000 --- a/netty-http-rx/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -dependencies { - api "io.reactivex:rxjava:${project.property('reactivex.version')}" - implementation "io.netty:netty-codec-http:${project.property('netty.version')}" - implementation "io.netty:netty-transport-native-epoll:${project.property('netty.version')}" - testImplementation "org.mockito:mockito-core:${project.property('mockito.version')}" - testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${project.property('junit.version')}" - testImplementation "junit:junit:${project.property('junit4.version')}" -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/HandlerNames.java b/netty-http-rx/src/main/java/io/reactivex/netty/HandlerNames.java deleted file mode 100644 index 14bf1f7..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/HandlerNames.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty; - -/** - * A list of all handler names added by the framework. This is just to ensure consistency in naming. - */ -public enum HandlerNames { - - SslHandler("ssl-handler"), - SslConnectionEmissionHandler("ssl-connection-emitter"), - WireLogging("wire-logging-handler"), - WriteTransformer("write-transformer"), - ClientReadTimeoutHandler("client-read-timeout-handler"), - ClientChannelActiveBufferingHandler("client-channel-active-buffer-handler"), - ; - - private final String name; - - HandlerNames(String name) { - this.name = qualify(name); - } - - public String getName() { - return name; - } - - private static String qualify(String name) { - return "_rx_netty_" + name; - - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/RxNetty.java b/netty-http-rx/src/main/java/io/reactivex/netty/RxNetty.java deleted file mode 100644 index 1eb4986..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/RxNetty.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty; - -import io.reactivex.netty.threads.RxEventLoopProvider; -import io.reactivex.netty.threads.SingleNioLoopProvider; - -public final class RxNetty { - - private static volatile RxEventLoopProvider rxEventLoopProvider = new SingleNioLoopProvider(Runtime.getRuntime().availableProcessors()); - - private static volatile boolean usingNativeTransport; - private static volatile boolean disableEventPublishing; - - private RxNetty() { - } - - /** - * An implementation of {@link RxEventLoopProvider} to be used by all clients and servers created after this call. - * - * @param provider New provider to use. - * - * @return Existing provider. - */ - public static RxEventLoopProvider useEventLoopProvider(RxEventLoopProvider provider) { - RxEventLoopProvider oldProvider = rxEventLoopProvider; - rxEventLoopProvider = provider; - return oldProvider; - } - - public static RxEventLoopProvider getRxEventLoopProvider() { - return rxEventLoopProvider; - } - - /** - * A global flag to start using netty's native protocol - * if applicable for a client or server. - * - * This does not evaluate whether the native transport is available for the OS or not. - * - * So, this method should be called conditionally when the caller is sure that the OS supports the native protocol. - * - * Alternatively, this can be done selectively per client and server instance. - */ - public static void useNativeTransportIfApplicable() { - usingNativeTransport = true; - } - - /** - * A global flag to disable the effects of calling {@link #useNativeTransportIfApplicable()} - */ - public static void disableNativeTransport() { - usingNativeTransport = false; - } - - /** - * Enables publishing of events for RxNetty. - */ - public static void enableEventPublishing() { - disableEventPublishing = false; - } - - /** - * Disables publishing of events for RxNetty. - */ - public static void disableEventPublishing() { - disableEventPublishing = true; - } - - /** - * Returns {@code true} if event publishing is disabled. - * - * @return {@code true} if event publishing is disabled. - */ - public static boolean isEventPublishingDisabled() { - return disableEventPublishing; - } - - public static boolean isUsingNativeTransport() { - return usingNativeTransport; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/AbstractConnectionToChannelBridge.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/AbstractConnectionToChannelBridge.java deleted file mode 100644 index e5bd22c..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/AbstractConnectionToChannelBridge.java +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOption; -import io.netty.util.AttributeKey; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.internal.EmptyArrays; -import io.reactivex.netty.channel.events.ConnectionEventListener; -import io.reactivex.netty.events.EventPublisher; -import java.util.logging.Level; -import java.util.logging.Logger; -import rx.Producer; -import rx.Subscriber; -import rx.exceptions.MissingBackpressureException; - -import java.nio.channels.ClosedChannelException; -import java.util.concurrent.atomic.AtomicLongFieldUpdater; - -/** - * A bridge between a {@link Connection} instance and the associated {@link Channel}. - * - * All operations on {@link Connection} will pass through this bridge to an appropriate action on the {@link Channel} - * - *

Lazy {@link Connection#getInput()} subscription

- * - * Lazy subscriptions are allowed on {@link Connection#getInput()} if and only if the channel is configured to - * not read data automatically (i.e. {@link ChannelOption#AUTO_READ} is set to {@code false}). Otherwise, - * if {@link Connection#getInput()} is subscribed lazily, the subscriber always receives an error. The content - * in this case is disposed upon reading. - * - * @param Type read from the connection held by this handler. - * @param Type written to the connection held by this handler. - */ -public abstract class AbstractConnectionToChannelBridge extends BackpressureManagingHandler { - - private static final Logger logger = Logger.getLogger(AbstractConnectionToChannelBridge.class.getName()); - - @SuppressWarnings("ThrowableInstanceNeverThrown") - private static final IllegalStateException ONLY_ONE_CONN_SUB_ALLOWED = - new IllegalStateException("Only one subscriber allowed for connection observable."); - @SuppressWarnings("ThrowableInstanceNeverThrown") - private static final IllegalStateException ONLY_ONE_CONN_INPUT_SUB_ALLOWED = - new IllegalStateException("Only one subscriber allowed for connection input."); - @SuppressWarnings("ThrowableInstanceNeverThrown") - private static final IllegalStateException LAZY_CONN_INPUT_SUB = - new IllegalStateException("Channel is set to auto-read but the subscription was lazy."); - - @SuppressWarnings("ThrowableInstanceNeverThrown") - private static final ClosedChannelException CLOSED_CHANNEL_EXCEPTION = new ClosedChannelException(); - - static { - ONLY_ONE_CONN_INPUT_SUB_ALLOWED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE); - ONLY_ONE_CONN_SUB_ALLOWED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE); - LAZY_CONN_INPUT_SUB.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE); - CLOSED_CHANNEL_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE); - } - - private final AttributeKey eventListenerAttributeKey; - private final AttributeKey eventPublisherAttributeKey; - - protected ConnectionEventListener eventListener; - protected EventPublisher eventPublisher; - private Subscriber newChannelSub; - private ReadProducer readProducer; - private boolean raiseErrorOnInputSubscription; - private boolean connectionEmitted; - - protected AbstractConnectionToChannelBridge(String thisHandlerName, ConnectionEventListener eventListener, - EventPublisher eventPublisher) { - super(thisHandlerName); - if (null == eventListener) { - throw new IllegalArgumentException("Event listener can not be null."); - } - if (null == eventPublisher) { - throw new IllegalArgumentException("Event publisher can not be null."); - } - this.eventListener = eventListener; - this.eventPublisher = eventPublisher; - eventListenerAttributeKey = null; - eventPublisherAttributeKey = null; - } - - protected AbstractConnectionToChannelBridge(String thisHandlerName, - AttributeKey eventListenerAttributeKey, - AttributeKey eventPublisherAttributeKey) { - super(thisHandlerName); - this.eventListenerAttributeKey = eventListenerAttributeKey; - this.eventPublisherAttributeKey = eventPublisherAttributeKey; - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - if (null == eventListener && null == eventPublisher) { - eventListener = ctx.channel().attr(eventListenerAttributeKey).get(); - eventPublisher = ctx.channel().attr(eventPublisherAttributeKey).get(); - } - - if (null == eventPublisher) { - logger.log(Level.SEVERE, "No Event publisher bound to the channel, closing channel."); - ctx.channel().close(); - return; - } - - if (eventPublisher.publishingEnabled() && null == eventListener) { - logger.log(Level.SEVERE, "No Event listener bound to the channel and publising is enabled, closing channel."); - ctx.channel().close(); - return; - } - - ctx.pipeline().addFirst(new BytesInspector(eventPublisher, eventListener)); - - super.handlerAdded(ctx); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - if (!connectionEmitted && isValidToEmit(newChannelSub)) { - emitNewConnection(ctx.channel()); - connectionEmitted = true; - } - super.channelInactive(ctx); - } - - @Override - public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { - - if (isValidToEmitToReadSubscriber(readProducer)) { - /*If the subscriber is still active, then it expects data but the channel is closed.*/ - readProducer.sendOnError(CLOSED_CHANNEL_EXCEPTION); - } - - super.channelUnregistered(ctx); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof EmitConnectionEvent) { - if (!connectionEmitted) { - emitNewConnection(ctx.channel()); - connectionEmitted = true; - } - } else if (evt instanceof ConnectionCreationFailedEvent) { - if (isValidToEmit(newChannelSub)) { - newChannelSub.onError(((ConnectionCreationFailedEvent)evt).getThrowable()); - } - } else if (evt instanceof ChannelSubscriberEvent) { - @SuppressWarnings("unchecked") - final ChannelSubscriberEvent channelSubscriberEvent = (ChannelSubscriberEvent) evt; - - newConnectionSubscriber(channelSubscriberEvent); - } else if (evt instanceof ConnectionInputSubscriberEvent) { - @SuppressWarnings("unchecked") - ConnectionInputSubscriberEvent event = (ConnectionInputSubscriberEvent) evt; - - newConnectionInputSubscriber(ctx.channel(), event.getSubscriber(), false); - } else if (evt instanceof ConnectionInputSubscriberResetEvent) { - resetConnectionInputSubscriber(); - } else if (evt instanceof ConnectionInputSubscriberReplaceEvent) { - @SuppressWarnings("unchecked") - ConnectionInputSubscriberReplaceEvent event = (ConnectionInputSubscriberReplaceEvent) evt; - replaceConnectionInputSubscriber(ctx.channel(), event); - } - - super.userEventTriggered(ctx, evt); - } - - @SuppressWarnings("unchecked") - @Override - public void newMessage(ChannelHandlerContext ctx, Object msg) { - if (isValidToEmitToReadSubscriber(readProducer)) { - try { - readProducer.sendOnNext((R) msg); - } catch (ClassCastException e) { - ReferenceCountUtil.release(msg); // Since, this was not sent to the subscriber, release the msg. - readProducer.sendOnError(e); - } - } else { - logger.log(Level.WARNING, "Data received on channel, but no subscriber registered. Discarding data. Message class: " - + msg.getClass().getName() + ", channel: " + ctx.channel()); - ReferenceCountUtil.release(msg); // No consumer of the message, so discard. - } - } - - @Override - public boolean shouldReadMore(ChannelHandlerContext ctx) { - return null != readProducer && readProducer.shouldReadMore(ctx); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (!connectionEmitted && isValidToEmit(newChannelSub)) { - newChannelSub.onError(cause); - } else if (isValidToEmitToReadSubscriber(readProducer)) { - readProducer.sendOnError(cause); - } else { - logger.log(Level.INFO, "Exception in the pipeline and none of the subscribers are active.", cause); - } - } - - protected static boolean isValidToEmit(Subscriber subscriber) { - return null != subscriber && !subscriber.isUnsubscribed(); - } - - private static boolean isValidToEmitToReadSubscriber(ReadProducer readProducer) { - return null != readProducer && !readProducer.subscriber.isUnsubscribed(); - } - - protected boolean connectionInputSubscriberExists(Channel channel) { - assert channel.eventLoop().inEventLoop(); - - return null != readProducer && null != readProducer.subscriber && !readProducer.subscriber.isUnsubscribed(); - } - - protected void onNewReadSubscriber(Subscriber subscriber) { - // NOOP - } - - protected final void checkEagerSubscriptionIfConfigured(Channel channel) { - if (channel.config().isAutoRead() && null == readProducer) { - // If the channel is set to auto-read and there is no eager subscription then, we should raise errors - // when a subscriber arrives. - raiseErrorOnInputSubscription = true; - final Subscriber discardAll = ConnectionInputSubscriberEvent.discardAllInput() - .getSubscriber(); - final ReadProducer producer = new ReadProducer<>(discardAll, channel); - discardAll.setProducer(producer); - readProducer = producer; - } - } - - protected final Subscriber getNewChannelSub() { - return newChannelSub; - } - - private void emitNewConnection(Channel channel) { - if (isValidToEmit(newChannelSub)) { - try { - newChannelSub.onNext(channel); - connectionEmitted = true; - checkEagerSubscriptionIfConfigured(channel); - newChannelSub.onCompleted(); - } catch (Exception e) { - logger.log(Level.SEVERE, "Error emitting a new connection. Closing this channel.", e); - channel.close(); - } - } else { - channel.close(); // Closing the connection if not sent to a subscriber. - } - } - - private void resetConnectionInputSubscriber() { - final Subscriber connInputSub = null == readProducer? null : readProducer.subscriber; - if (isValidToEmit(connInputSub)) { - connInputSub.onCompleted(); - } - raiseErrorOnInputSubscription = false; - readProducer = null; // A subsequent event should set it to the desired subscriber. - } - - private void newConnectionInputSubscriber(final Channel channel, final Subscriber subscriber, - boolean replace) { - final Subscriber connInputSub = null == readProducer ? null : readProducer.subscriber; - if (isValidToEmit(connInputSub)) { - if (!replace) { - /*Allow only once concurrent input subscriber but allow concatenated subscribers*/ - subscriber.onError(ONLY_ONE_CONN_INPUT_SUB_ALLOWED); - } else { - setNewReadProducer(channel, subscriber); - connInputSub.onCompleted(); - } - } else if (raiseErrorOnInputSubscription) { - subscriber.onError(LAZY_CONN_INPUT_SUB); - } else { - setNewReadProducer(channel, subscriber); - } - } - - private void setNewReadProducer(Channel channel, Subscriber subscriber) { - final ReadProducer producer = new ReadProducer<>(subscriber, channel); - subscriber.setProducer(producer); - onNewReadSubscriber(subscriber); - readProducer = producer; - } - - private void replaceConnectionInputSubscriber(Channel channel, ConnectionInputSubscriberReplaceEvent event) { - ConnectionInputSubscriberEvent newSubEvent = event.getNewSubEvent(); - newConnectionInputSubscriber(channel, newSubEvent.getSubscriber(), - true); - } - - private void newConnectionSubscriber(ChannelSubscriberEvent event) { - if (null == newChannelSub) { - newChannelSub = event.getSubscriber(); - } else { - event.getSubscriber().onError(ONLY_ONE_CONN_SUB_ALLOWED); - } - } - - /*Visible for testing*/ static final class ReadProducer extends RequestReadIfRequiredEvent implements Producer { - - @SuppressWarnings("rawtypes") - private static final AtomicLongFieldUpdater REQUEST_UPDATER = - AtomicLongFieldUpdater.newUpdater(ReadProducer.class, "requested");/*Updater for requested*/ - private volatile long requested; // Updated by REQUEST_UPDATER, required to be volatile. - - private final Subscriber subscriber; - private final Channel channel; - - /*Visible for testing*/ ReadProducer(Subscriber subscriber, Channel channel) { - this.subscriber = subscriber; - this.channel = channel; - } - - @Override - public void request(long n) { - if (Long.MAX_VALUE != requested) { - if (Long.MAX_VALUE == n) { - // Now turning off backpressure - REQUEST_UPDATER.set(this, Long.MAX_VALUE); - } else { - // add n to field but check for overflow - while (true) { - final long current = requested; - long next = current + n; - // check for overflow - if (next < 0) { - next = Long.MAX_VALUE; - } - if (REQUEST_UPDATER.compareAndSet(this, current, next)) { - break; - } - } - } - } - - if (!channel.config().isAutoRead()) { - channel.pipeline().fireUserEventTriggered(this); - } - } - - public void sendOnError(Throwable throwable) { - subscriber.onError(throwable); - } - - public void sendOnComplete() { - subscriber.onCompleted(); - } - - public void sendOnNext(T nextItem) { - if (requested > 0) { - if (REQUEST_UPDATER.get(this) != Long.MAX_VALUE) { - REQUEST_UPDATER.decrementAndGet(this); - } - subscriber.onNext(nextItem); - } else { - subscriber.onError(new MissingBackpressureException( - "Received more data on the channel than demanded by the subscriber.")); - } - } - - @Override - protected boolean shouldReadMore(ChannelHandlerContext ctx) { - return !subscriber.isUnsubscribed() && REQUEST_UPDATER.get(this) > 0; - } - - /*Visible for testing*/long getRequested() { - return requested; - } - - @Override - public String toString() { - return "ReadProducer{" + "requested=" + requested + '}'; - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/AllocatingTransformer.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/AllocatingTransformer.java deleted file mode 100644 index 01b0fa2..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/AllocatingTransformer.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.util.internal.TypeParameterMatcher; -import rx.annotations.Beta; - -import java.util.List; - -/** - * A transformer to be used for modifying the type of objects written on a {@link Connection}. - * - *

Why is this required?

- * - * The type of an object can usually be transformed using {@code Observable.map()}, however, while writing on a - * {@link Connection}, typically one requires to allocate buffers. Although a {@code Connection} provides a way to - * retrieve the {@link ByteBufAllocator} via the {@code Channel}, allocating buffers from outside the eventloop will - * lead to buffer bloats as the allocators will typically use thread-local buffer pools.

- * - * This transformer is always invoked from within the eventloop and hence does not have buffer bloating issues, even - * when transformations happen outside the eventloop. - * - * @param Source type. - * @param Target type. - */ -@Beta -public abstract class AllocatingTransformer { - - private final TypeParameterMatcher matcher; - - protected AllocatingTransformer() { - matcher = TypeParameterMatcher.find(this, AllocatingTransformer.class, "T"); - } - - /** - * Asserts whether the passed message can be transformed using this transformer. - * - * @param msg Message to transform. - * - * @return {@code true} if the message can be transformed. - */ - protected boolean acceptMessage(Object msg) { - return matcher.match(msg); - } - - /** - * Transforms the passed message and adds the output to the returned list. - * - * @param toTransform Message to transform. - * @param allocator Allocating for allocating buffers, if required. - * - * @return Output of the transformation. - */ - public abstract List transform(T toTransform, ByteBufAllocator allocator); - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/AppendTransformerEvent.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/AppendTransformerEvent.java deleted file mode 100644 index 9cbc39e..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/AppendTransformerEvent.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -/** - * An event to register a custom transformer of data written on a channel. - * - * @param Source type for the transformer. - * @param Target type for the transformer. - */ -public final class AppendTransformerEvent { - - private final AllocatingTransformer transformer; - - public AppendTransformerEvent(AllocatingTransformer transformer) { - if (null == transformer) { - throw new NullPointerException("Transformer can not be null."); - } - this.transformer = transformer; - } - - public AllocatingTransformer getTransformer() { - return transformer; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/AutoReleaseOperator.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/AutoReleaseOperator.java deleted file mode 100644 index dcf977f..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/AutoReleaseOperator.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.util.ReferenceCountUtil; -import rx.Observable.Operator; -import rx.Subscriber; - -class AutoReleaseOperator implements Operator { - - @Override - public Subscriber call(final Subscriber subscriber) { - return new Subscriber(subscriber) { - @Override - public void onCompleted() { - subscriber.onCompleted(); - } - - @Override - public void onError(Throwable e) { - subscriber.onError(e); - } - - @Override - public void onNext(T t) { - try { - subscriber.onNext(t); - } finally { - ReferenceCountUtil.release(t); - } - } - }; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/BackpressureManagingHandler.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/BackpressureManagingHandler.java deleted file mode 100644 index 6b7a53a..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/BackpressureManagingHandler.java +++ /dev/null @@ -1,710 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.internal.RecyclableArrayList; -import java.util.logging.Level; -import java.util.logging.Logger; -import rx.Observable; -import rx.Scheduler; -import rx.Subscriber; -import rx.functions.Action0; -import rx.schedulers.Schedulers; -import rx.subscriptions.Subscriptions; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.ConcurrentLinkedQueue; - -public abstract class BackpressureManagingHandler extends ChannelDuplexHandler { - - private static final Logger logger = Logger.getLogger(BackpressureManagingHandler.class.getName()); - - /*Visible for testing*/ enum State { - ReadRequested, - Reading, - Buffering, - DrainingBuffer, - Stopped, - } - - private RecyclableArrayList buffer; - private int currentBufferIndex; - private State currentState = State.Buffering; /*Buffer unless explicitly asked to read*/ - private boolean continueDraining; - private final BytesWriteInterceptor bytesWriteInterceptor; - - protected BackpressureManagingHandler(String thisHandlerName) { - bytesWriteInterceptor = new BytesWriteInterceptor(thisHandlerName); - } - - @SuppressWarnings("fallthrough") - @Override - public final void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - - if (State.Stopped != currentState && !shouldReadMore(ctx)) { - currentState = State.Buffering; - } - - switch (currentState) { - case ReadRequested: - currentState = State.Reading; - case Reading: - newMessage(ctx, msg); - break; - case Buffering: - case DrainingBuffer: - if (null == buffer) { - buffer = RecyclableArrayList.newInstance(); - } - buffer.add(msg); - break; - case Stopped: - logger.log(Level.WARNING, "Message read after handler removed, discarding the same. Message class: " - + msg.getClass().getName()); - ReferenceCountUtil.release(msg); - break; - } - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - ctx.pipeline().addFirst(bytesWriteInterceptor); - currentState = State.Buffering; - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - /*On shut down, all the handlers are removed from the pipeline, so we don't need to explicitly remove the - additional handlers added in handlerAdded()*/ - currentState = State.Stopped; - if (null != buffer) { - if (!buffer.isEmpty()) { - for (Object item : buffer) { - ReferenceCountUtil.release(item); - } - } - buffer.recycle(); - buffer = null; - } - } - - @Override - public final void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - - switch (currentState) { - case ReadRequested: - /*Nothing read from the last request, forward to read() and let it take the decision on what to do.*/ - break; - case Reading: - /* - * After read completion, move to Buffering, unless an explicit read is issued, which moves to an - * appropriate state. - */ - currentState = State.Buffering; - break; - case Buffering: - /*Keep buffering, unless the buffer drains and more items are requested*/ - break; - case DrainingBuffer: - /*Keep draining, unless the buffer drains and more items are requested*/ - break; - case Stopped: - break; - } - - ctx.fireChannelReadComplete(); - - if (!ctx.channel().config().isAutoRead() && shouldReadMore(ctx)) { - read(ctx); - } - } - - @Override - public final void read(ChannelHandlerContext ctx) throws Exception { - switch (currentState) { - case ReadRequested: - /*Nothing read since last request, but requested more, so push the demand upstream.*/ - ctx.read(); - break; - case Reading: - /* - * We are already reading data and the read has not completed as that would move the state to buffering. - * So, ignore this read, or otherwise, read is requested on the channel, unnecessarily. - */ - break; - case Buffering: - /* - * We were buffering and now a read was requested, so start draining the buffer. - */ - currentState = State.DrainingBuffer; - continueDraining = true; - /* - * Looping here to drain, instead of having it done via readComplete -> read -> readComplete loop to reduce - * call stack depth. Otherwise, the stackdepth is proportional to number of items in the buffer and hence - * for large buffers will overflow stack. - */ - while (continueDraining && null != buffer && currentBufferIndex < buffer.size()) { - Object nextItem = buffer.get(currentBufferIndex++); - newMessage(ctx, nextItem); /*Send the next message.*/ - /* - * If there is more read demand then that should come as part of read complete or later as another - * read (this method) invocation. */ - continueDraining = false; - channelReadComplete(ctx); - } - - if (continueDraining) { - if (null != buffer) { - /*Outstanding read demand and buffer is empty, so recycle the buffer and pass the read upstream.*/ - recycleBuffer(); - } - /* - * Since, continueDraining is true and we have broken out of the drain loop, it means that there are no - * items in the buffer and there is more read demand. Switch to read requested and send the read demand - * downstream. - */ - currentState = State.ReadRequested; - ctx.read(); - } else { - /* - * There is no more demand, so set the state to buffering and so another read invocation can start - * draining. - */ - currentState = State.Buffering; - /*If buffer is empty, then recycle.*/ - if (null != buffer && currentBufferIndex >= buffer.size()) { - recycleBuffer(); - } - } - break; - case DrainingBuffer: - /*Already draining buffer, so break the call stack and let the caller keep draining.*/ - continueDraining = true; - break; - case Stopped: - /*Invalid, pass it downstream.*/ - ctx.read(); - break; - } - } - - /** - * Intercepts a write on the channel. The following message types are handled: - * - *

    -
  • String: If the pipeline is not configured to write a String, this converts the string to a {@link io.netty.buffer.ByteBuf} and - then writes it on the channel.
  • -
  • byte[]: If the pipeline is not configured to write a byte[], this converts the byte[] to a {@link io.netty.buffer.ByteBuf} and - then writes it on the channel.
  • -
  • Observable: Subscribes to the {@link Observable} and writes all items, requesting the next item if and only if - the channel is writable as indicated by {@link Channel#isWritable()}
  • -
- * - * @param ctx Channel handler context. - * @param msg Message to write. - * @param promise Promise for the completion of write. - * - * @throws Exception If there is an error handling this write. - */ - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - if (msg instanceof Observable) { - @SuppressWarnings("rawtypes") - Observable observable = (Observable) msg; /*One can write heterogeneous objects on a channel.*/ - final WriteStreamSubscriber subscriber = bytesWriteInterceptor.newSubscriber(ctx, promise); - subscriber.subscribeTo(observable); - } else { - ctx.write(msg, promise); - } - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof RequestReadIfRequiredEvent) { - RequestReadIfRequiredEvent requestReadIfRequiredEvent = (RequestReadIfRequiredEvent) evt; - if (requestReadIfRequiredEvent.shouldReadMore(ctx)) { - read(ctx); - } - } - - super.userEventTriggered(ctx, evt); - } - - protected abstract void newMessage(ChannelHandlerContext ctx, Object msg); - - protected abstract boolean shouldReadMore(ChannelHandlerContext ctx); - - /*Visible for testing*/ RecyclableArrayList getBuffer() { - return buffer; - } - - /*Visible for testing*/ int getCurrentBufferIndex() { - return currentBufferIndex; - } - - /*Visible for testing*/ State getCurrentState() { - return currentState; - } - - private void recycleBuffer() { - buffer.recycle(); - currentBufferIndex = 0; - buffer = null; - } - - protected static abstract class RequestReadIfRequiredEvent { - - protected abstract boolean shouldReadMore(ChannelHandlerContext ctx); - } - - /** - * This handler inspects write to see if a write made it to {@link BytesWriteInterceptor} inline with a write call. - * The reasons why a write would not make it to the channel, would be: - *
    -
  • If there is a handler in the pipeline that runs in a different group.
  • -
  • If there is a handler that collects many items to produce a single item.
  • -
- * - * When a write did not reach the {@link BytesWriteInterceptor}, no request for more items will be generated and - * we could get into a deadlock where a handler is waiting for more items (collect case) but no more items arrive as - * no more request is generated. In order to avoid this deadlock, this handler will detect the situation and - * trigger more request in this case. - * - * Why a separate handler? - * - * This needs to be different than {@link BytesWriteInterceptor} as we need it immediately after - * {@link BackpressureManagingHandler} so that no other handler eats a write and {@link BytesWriteInterceptor} is - * always the first handler in the pipeline to be right before the channel and hence maintain proper demand. - */ - static final class WriteInspector extends ChannelDuplexHandler { - - private final BytesWriteInterceptor bytesWriteInterceptor; - - WriteInspector(BytesWriteInterceptor bytesWriteInterceptor) { - this.bytesWriteInterceptor = bytesWriteInterceptor; - } - - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - /*Both these handlers always run in the same executor, so it's safe to access this variable.*/ - bytesWriteInterceptor.messageReceived = false; /*reset flag for this write*/ - ctx.write(msg, promise); - if (!bytesWriteInterceptor.messageReceived) { - bytesWriteInterceptor.requestMoreIfWritable(ctx.channel()); - } - } - } - - /** - * Regulates write->request more->write process on the channel. - * - * Why is this a separate handler? - * The sole purpose of this handler is to request more items from each of the Observable streams producing items to - * write. It is important to request more items only when the current item is written on the channel i.e. added to - * the ChannelOutboundBuffer. If we request more from outside the pipeline (from WriteStreamSubscriber.onNext()) - * then it may so happen that the onNext is not from within this eventloop and hence instead of being written to - * the channel, is added to the task queue of the EventLoop. Requesting more items in such a case, would mean we - * keep adding the writes to the eventloop queue and not on the channel buffer. This would mean that the channel - * writability would not truly indicate the buffer. - */ - /*Visible for testing*/ static final class BytesWriteInterceptor extends ChannelDuplexHandler implements Runnable { - - /*Visible for testing*/ static final String WRITE_INSPECTOR_HANDLER_NAME = "write-inspector"; - /*Visible for testing*/ static final int MAX_PER_SUBSCRIBER_REQUEST = 64; - - /* - * Since, unsubscribes can happen on a different thread, this has to be thread-safe. - */ - private final ConcurrentLinkedQueue subscribers = new ConcurrentLinkedQueue<>(); - private final String parentHandlerName; - - /* This should always be access from the eventloop and can be used to manage state before and after a write to - * see if a write started from {@link WriteInspector} made it to this handler. - */ - private boolean messageReceived; - - /** - * The intent here is to equally divide the request to all subscribers but do not put a hard-bound on whether - * the subscribers are actually adhering to the limit (by not throwing MissingBackpressureException). This keeps - * the request distribution simple and still give opprotunities for subscribers to optimize (increase the limit) - * if there is a signal that the consumption is slower than the producer. - * - * Worst case of this scheme is request-1 per subscriber which happens when there are as many subscribers as - * the max limit. - */ - private int perSubscriberMaxRequest = MAX_PER_SUBSCRIBER_REQUEST; - private Channel channel; - private boolean removeTaskScheduled; // Guarded by this - - BytesWriteInterceptor(String parentHandlerName) { - this.parentHandlerName = parentHandlerName; - } - - @Override - public void write(ChannelHandlerContext ctx, final Object msg, ChannelPromise promise) throws Exception { - ctx.write(msg, promise); - messageReceived = true; - requestMoreIfWritable(ctx.channel()); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - channel = ctx.channel(); - WriteInspector writeInspector = new WriteInspector(this); - ChannelHandler parent = ctx.pipeline().get(parentHandlerName); - if (null != parent) { - ctx.pipeline().addBefore(parentHandlerName, WRITE_INSPECTOR_HANDLER_NAME, writeInspector); - } - } - - @Override - public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { - if (ctx.channel().isWritable()) { - requestMoreIfWritable(ctx.channel()); - } - super.channelWritabilityChanged(ctx); - } - - public WriteStreamSubscriber newSubscriber(final ChannelHandlerContext ctx, ChannelPromise promise) { - int currentSubCount = subscribers.size(); - recalculateMaxPerSubscriber(currentSubCount, currentSubCount + 1); - - final WriteStreamSubscriber sub = new WriteStreamSubscriber(ctx, promise, perSubscriberMaxRequest); - sub.add(Subscriptions.create(new Action0() { - @Override - public void call() { - boolean _schedule; - /*Schedule the task once as the task runs through and removes all unsubscribed subscribers*/ - synchronized (BytesWriteInterceptor.this) { - _schedule = !removeTaskScheduled; - removeTaskScheduled = true; - } - if (_schedule) { - ctx.channel().eventLoop().execute(BytesWriteInterceptor.this); - } - } - })); - - subscribers.add(sub); - return sub; - } - - /*Visible for testing*/List getSubscribers() { - return Collections.unmodifiableList(new ArrayList<>(subscribers)); - } - - private void requestMoreIfWritable(Channel channel) { - assert channel.eventLoop().inEventLoop(); - - for (WriteStreamSubscriber subscriber: subscribers) { - if (!subscriber.isUnsubscribed() && channel.isWritable()) { - subscriber.requestMoreIfNeeded(perSubscriberMaxRequest); - } - } - } - - @Override - public void run() { - synchronized (this) { - removeTaskScheduled = false; - } - int oldSubCount = subscribers.size(); - for (Iterator iterator = subscribers.iterator(); iterator.hasNext(); ) { - WriteStreamSubscriber subscriber = iterator.next(); - if (subscriber.isUnsubscribed()) { - iterator.remove(); - } - } - int newSubCount = subscribers.size(); - recalculateMaxPerSubscriber(oldSubCount, newSubCount); - } - - /** - * Called from within the eventloop, whenever the subscriber queue is modified. This modifies the per subscriber - * request limit by equally distributing the demand. Minimum demand to any subscriber is 1. - */ - private void recalculateMaxPerSubscriber(int oldSubCount, int newSubCount) { - assert channel.eventLoop().inEventLoop(); - perSubscriberMaxRequest = newSubCount == 0 || oldSubCount == 0 - ? MAX_PER_SUBSCRIBER_REQUEST - : perSubscriberMaxRequest * oldSubCount / newSubCount; - - perSubscriberMaxRequest = Math.max(1, perSubscriberMaxRequest); - - if (logger.isLoggable(Level.FINE)) { - logger.log(Level.FINE, "Channel " + channel + - " modifying per subscriber max request. Old subscribers count " + oldSubCount + - " new subscribers count " + newSubCount + - " new Value {} " + perSubscriberMaxRequest); - } - } - } - - /** - * Backpressure enabled subscriber to an Observable written on this channel. This connects the promise for writing - * the Observable to all the promises created per write (per onNext). - */ - /*Visible for testing*/static class WriteStreamSubscriber extends Subscriber { - - private final ChannelHandlerContext ctx; - private final ChannelPromise overarchingWritePromise; - - private final int initialRequest; - private long maxBufferSize; - private long pending; /*Guarded by guard*/ - private long lowWaterMark; - - private final Object guard = new Object(); - private boolean isDone; /*Guarded by guard*/ - private Scheduler.Worker writeWorker; /*Guarded by guard*/ - private boolean atleastOneWriteEnqueued; /*Guarded by guard*/ - private int enqueued; /*Guarded by guard*/ - - private boolean isPromiseCompletedOnWriteComplete; /*Guarded by guard. Only transition should be false->true*/ - - private int listeningTo; - - /*Visible for testing*/ WriteStreamSubscriber(ChannelHandlerContext ctx, ChannelPromise promise, - int initialRequest) { - this.ctx = ctx; - overarchingWritePromise = promise; - this.initialRequest = initialRequest; - promise.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (future.isCancelled()) { - unsubscribe(); /*Unsubscribe from source if the promise is cancelled.*/ - } - } - }); - } - - @Override - public void onStart() { - requestMoreIfNeeded(initialRequest); - } - - @Override - public void onCompleted() { - onTermination(null); - } - - @Override - public void onError(Throwable e) { - onTermination(e); - } - - @Override - public void onNext(Object nextItem) { - final boolean enqueue; - boolean inEL = ctx.channel().eventLoop().inEventLoop(); - - synchronized (guard) { - pending--; - if (null == writeWorker) { - if (!inEL) { - atleastOneWriteEnqueued = true; - } - if (atleastOneWriteEnqueued) { - writeWorker = Schedulers.computation().createWorker(); - } - } - - enqueue = null != writeWorker && (inEL || enqueued > 0); - - if (enqueue) { - enqueued++; - } - } - - final ChannelFuture channelFuture = enqueue ? enqueueWrite(nextItem) : ctx.write(nextItem); - - synchronized (guard) { - listeningTo++; - } - - channelFuture.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - - if (overarchingWritePromise.isDone()) { - /* - * Overarching promise will be done if and only if there was an error or all futures have - * completed. In both cases, this callback is useless, hence return from here. - * IOW, if we are here, it can be two cases: - * - * - There has already been a write that has failed. So, the promise is done with failure. - * - There was a write that arrived after termination of the Observable. - * - * Two above isn't possible as per Rx contract. - * One above is possible but is not of any consequence w.r.t this listener as this listener does - * not give callbacks to specific writes - */ - return; - } - - boolean _isPromiseCompletedOnWriteComplete; - - /* - * The intent here is to NOT give listener callbacks via promise completion within the sync block. - * So, a co-ordination b/w the thread sending Observable terminal event and thread sending write - * completion event is required. - * The only work to be done in the Observable terminal event thread is to whether the - * overarchingWritePromise is to be completed or not. - * The errors are not buffered, so the overarchingWritePromise is completed in this callback w/o - * knowing whether any more writes will arive or not. - * This co-oridantion is done via the flag isPromiseCompletedOnWriteComplete - */ - synchronized (guard) { - listeningTo--; - if (0 == listeningTo && isDone) { - /* - * If the listening count is 0 and no more items will arrive, this thread wins the race of - * completing the overarchingWritePromise - */ - isPromiseCompletedOnWriteComplete = true; - } - _isPromiseCompletedOnWriteComplete = isPromiseCompletedOnWriteComplete; - } - - /* - * Exceptions are not buffered but completion is only sent when there are no more items to be - * received for write. - */ - if (!future.isSuccess()) { - overarchingWritePromise.tryFailure(future.cause()); - /* - * Unsubscribe this subscriber when write fails as we are completing the promise which is - * attached to the listener of the write results. - */ - unsubscribe(); - } else if (_isPromiseCompletedOnWriteComplete) { /*Once set to true, never goes back to false.*/ - /*Complete only when no more items will arrive and all writes are completed*/ - overarchingWritePromise.trySuccess(); - } - } - }); - } - - private ChannelFuture enqueueWrite(final Object nextItem) { - final ChannelPromise toReturn = ctx.channel().newPromise(); - writeWorker.schedule(new Action0() { - @Override - public void call() { - ctx.write(nextItem, toReturn); - synchronized (guard) { - enqueued--; - } - } - }); - return toReturn; - } - - private void onTermination(Throwable throwableIfAny) { - int _listeningTo; - boolean _shouldCompletePromise; - final boolean enqueueFlush; - - /* - * The intent here is to NOT give listener callbacks via promise completion within the sync block. - * So, a co-ordination b/w the thread sending Observable terminal event and thread sending write - * completion event is required. - * The only work to be done in the Observable terminal event thread is to whether the - * overarchingWritePromise is to be completed or not. - * The errors are not buffered, so the overarchingWritePromise is completed in this callback w/o - * knowing whether any more writes will arive or not. - * This co-oridantion is done via the flag isPromiseCompletedOnWriteComplete - */ - synchronized (guard) { - enqueueFlush = atleastOneWriteEnqueued; - isDone = true; - _listeningTo = listeningTo; - /* - * Flag to indicate whether the write complete thread won the race and will complete the - * overarchingWritePromise - */ - _shouldCompletePromise = 0 == _listeningTo && !isPromiseCompletedOnWriteComplete; - } - - if (enqueueFlush) { - writeWorker.schedule(new Action0() { - @Override - public void call() { - ctx.flush(); - } - }); - } - - if (null != throwableIfAny) { - overarchingWritePromise.tryFailure(throwableIfAny); - } else { - if (_shouldCompletePromise) { - overarchingWritePromise.trySuccess(); - } - } - } - - /** - * Signals this subscriber to request more data from upstream, optionally modifying the max buffer size or max - * requests upstream. This will request more either if the new buffer size is greater than existing or pending - * items from upstream are less than the low water mark (which is half the max size). - * - * @param newMaxBufferSize New max buffer size, ignored if it is the same as existing. - */ - /*Visible for testing*/void requestMoreIfNeeded(long newMaxBufferSize) { - long toRequest = 0; - - synchronized (guard) { - if (newMaxBufferSize > maxBufferSize) { - // Applicable only when request up is not triggered by pending < lowWaterMark. - toRequest = newMaxBufferSize - maxBufferSize; - } - - maxBufferSize = newMaxBufferSize; - lowWaterMark = maxBufferSize / 2; - - if (pending < lowWaterMark) { - // Intentionally overwrites the existing toRequest as this includes all required changes. - toRequest = maxBufferSize - pending; - } - - pending += toRequest; - } - - if (toRequest > 0) { - request(toRequest); - } - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - public void subscribeTo(Observable observable) { - observable.subscribe(this); /*Need safe subscription as this is the subscriber and not a sub passed in*/ - } - } - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/BytesInspector.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/BytesInspector.java deleted file mode 100644 index 481aafb..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/BytesInspector.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.channel.FileRegion; -import io.reactivex.netty.channel.events.ConnectionEventListener; -import io.reactivex.netty.events.EventPublisher; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class BytesInspector extends ChannelDuplexHandler { - - private static final Logger logger = Logger.getLogger(BytesInspector.class.getName()); - - private final ConnectionEventListener eventListener; - private final EventPublisher eventPublisher; - - public BytesInspector(EventPublisher eventPublisher, ConnectionEventListener eventListener) { - this.eventPublisher = eventPublisher; - this.eventListener = eventListener; - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - try { - if (ByteBuf.class.isAssignableFrom(msg.getClass())) { - publishBytesRead((ByteBuf) msg); - } else if (ByteBufHolder.class.isAssignableFrom(msg.getClass())) { - ByteBufHolder holder = (ByteBufHolder) msg; - publishBytesRead(holder.content()); - } - } catch (Exception e) { - logger.log(Level.WARNING, "Failed to publish bytes read metrics event. This does *not* stop the pipeline processing.", e); - } finally { - super.channelRead(ctx, msg); - } - } - - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - try { - if (ByteBuf.class.isAssignableFrom(msg.getClass())) { - publishBytesWritten(((ByteBuf) msg).readableBytes(), promise); - } else if (ByteBufHolder.class.isAssignableFrom(msg.getClass())) { - publishBytesWritten(((ByteBufHolder)msg).content().readableBytes(), promise); - } else if (FileRegion.class.isAssignableFrom(msg.getClass())) { - publishBytesWritten(((FileRegion) msg).count(), promise); - } - } catch (Exception e) { - logger.log(Level.WARNING, "Failed to publish bytes write metrics event. This does *not* stop the pipeline processing.", e); - } finally { - super.write(ctx, msg, promise); - } - } - - @SuppressWarnings("unchecked") - protected void publishBytesWritten(final long bytesToWrite, ChannelPromise promise) { - if (bytesToWrite <= 0) { - return; - } - - if (eventPublisher.publishingEnabled()) { - promise.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - eventListener.onByteWritten(bytesToWrite); - } - }); - } - } - - @SuppressWarnings("unchecked") - protected void publishBytesRead(ByteBuf byteBuf) { - if (null != byteBuf) { - eventListener.onByteRead(byteBuf.readableBytes()); - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ChannelOperations.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/ChannelOperations.java deleted file mode 100644 index 4ee215a..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ChannelOperations.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.Channel; -import io.netty.channel.FileRegion; -import io.netty.util.AttributeKey; -import rx.Observable; -import rx.functions.Func1; - -/** - * A list of user initiated operations that can be done on a channel. - * - * @param Type of data that can be written on the associated channel. - */ -public interface ChannelOperations { - - /** - * Flush selector that always returns true. - */ - Func1 FLUSH_ON_EACH_STRING = new Func1() { - @Override - public Boolean call(String next) { - return true; - } - }; - - /** - * Flush selector that always returns true. - */ - Func1 FLUSH_ON_EACH_BYTES = new Func1() { - @Override - public Boolean call(byte[] next) { - return true; - } - }; - - /** - * Flush selector that always returns true. - */ - Func1 FLUSH_ON_EACH_FILE_REGION = new Func1() { - @Override - public Boolean call(FileRegion next) { - return true; - } - }; - AttributeKey FLUSH_ONLY_ON_READ_COMPLETE = - AttributeKey.valueOf("_rxnetyy-flush-only-on-read-complete"); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel. - * - * All writes will be flushed on completion of the passed {@code Observable} - * - * @param msgs Stream of messages to write. - * - * @return {@link Observable} representing the result of this write. Every subscription to this {@link Observable} - * will replay the write on the channel. - */ - Observable write(Observable msgs); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel - * and flushes the channel, everytime, {@code flushSelector} returns {@code true} . Any writes issued before - * subscribing, will also be flushed. However, the returned {@link Observable} will not capture the result of those - * writes, i.e. if the other writes, fail and this write does not, the returned {@link Observable} will not fail. - * - * @param msgs Message stream to write. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. Channel is - * flushed, iff this function returns, {@code true}. - * - * @return An {@link Observable} representing the result of this write. Every - * subscription to this {@link Observable} will write the passed messages and flush all pending writes, when the - * {@code flushSelector} returns {@code true} - */ - Observable write(Observable msgs, Func1 flushSelector); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel - * and flushes the channel, on every write. Any writes issued before subscribing, will also be flushed. However, the - * returned {@link Observable} will not capture the result of those writes, i.e. if the other writes, fail and this - * write does not, the returned {@link Observable} will not fail. - * - * @param msgs Message stream to write. - * - * @return An {@link Observable} representing the result of this write. Every - * subscription to this {@link Observable} will write the passed messages and flush all pending writes, on every - * write. - */ - Observable writeAndFlushOnEach(Observable msgs); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel. - * - * All writes will be flushed on completion of the passed {@code Observable} - * - * @param msgs Stream of messages to write. - * - * @return {@link Observable} representing the result of this write. Every subscription to this {@link Observable} - * will replay the write on the channel. - */ - Observable writeString(Observable msgs); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel - * and flushes the channel, everytime, {@code flushSelector} returns {@code true} . Any writes issued before - * subscribing, will also be flushed. However, the returned {@link Observable} will not capture the result of those - * writes, i.e. if the other writes, fail and this write does not, the returned {@link Observable} will not fail. - * - * @param msgs Message stream to write. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. Channel is - * flushed, iff this function returns, {@code true}. - * - * @return An {@link Observable} representing the result of this write. Every - * subscription to this {@link Observable} will write the passed messages and flush all pending writes, when the - * {@code flushSelector} returns {@code true} - */ - Observable writeString(Observable msgs, Func1 flushSelector); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel - * and flushes the channel, on every write. Any writes issued before subscribing, will also be flushed. However, the - * returned {@link Observable} will not capture the result of those writes, i.e. if the other writes, fail and this - * write does not, the returned {@link Observable} will not fail. - * - * @param msgs Message stream to write. - * - * @return An {@link Observable} representing the result of this write. Every - * subscription to this {@link Observable} will write the passed messages and flush all pending writes, on every - * write. - */ - Observable writeStringAndFlushOnEach(Observable msgs); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel. - * - * All writes will be flushed on completion of the passed {@code Observable} - * - * @param msgs Stream of messages to write. - * - * @return {@link Observable} representing the result of this write. Every subscription to this {@link Observable} - * will replay the write on the channel. - */ - Observable writeBytes(Observable msgs); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel - * and flushes the channel, everytime, {@code flushSelector} returns {@code true} . Any writes issued before - * subscribing, will also be flushed. However, the returned {@link Observable} will not capture the result of those - * writes, i.e. if the other writes, fail and this write does not, the returned {@link Observable} will not fail. - * - * @param msgs Message stream to write. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. Channel is - * flushed, iff this function returns, {@code true}. - * - * @return An {@link Observable} representing the result of this write. Every - * subscription to this {@link Observable} will write the passed messages and flush all pending writes, when the - * {@code flushSelector} returns {@code true} - */ - Observable writeBytes(Observable msgs, Func1 flushSelector); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel - * and flushes the channel, on every write. Any writes issued before subscribing, will also be flushed. However, the - * returned {@link Observable} will not capture the result of those writes, i.e. if the other writes, fail and this - * write does not, the returned {@link Observable} will not fail. - * - * @param msgs Message stream to write. - * - * @return An {@link Observable} representing the result of this write. Every - * subscription to this {@link Observable} will write the passed messages and flush all pending writes, on every - * write. - */ - Observable writeBytesAndFlushOnEach(Observable msgs); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel. - * - * All writes will be flushed on completion of the passed {@code Observable} - * - * @param msgs Stream of messages to write. - * - * @return {@link Observable} representing the result of this write. Every subscription to this {@link Observable} - * will replay the write on the channel. - */ - Observable writeFileRegion(Observable msgs); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel - * and flushes the channel, everytime, {@code flushSelector} returns {@code true} . Any writes issued before - * subscribing, will also be flushed. However, the returned {@link Observable} will not capture the result of those - * writes, i.e. if the other writes, fail and this write does not, the returned {@link Observable} will not fail. - * - * @param msgs Message stream to write. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. Channel is - * flushed, iff this function returns, {@code true}. - * - * @return An {@link Observable} representing the result of this write. Every - * subscription to this {@link Observable} will write the passed messages and flush all pending writes, when the - * {@code flushSelector} returns {@code true} - */ - Observable writeFileRegion(Observable msgs, Func1 flushSelector); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel - * and flushes the channel, on every write. Any writes issued before subscribing, will also be flushed. However, the - * returned {@link Observable} will not capture the result of those writes, i.e. if the other writes, fail and this - * write does not, the returned {@link Observable} will not fail. - * - * @param msgs Message stream to write. - * - * @return An {@link Observable} representing the result of this write. Every - * subscription to this {@link Observable} will write the passed messages and flush all pending writes, on every - * write. - */ - Observable writeFileRegionAndFlushOnEach(Observable msgs); - - /** - * Modifies the underneath channel to enable writing new type of objects that will be transformed using the passed - * {@code transformer} - * - * @param transformer Transformer to transform objects written to the channel. - * - * @param The target type of the transformer. - * - * @return A new instance of {@code ChannelOperations} that accepts the transformed type to write. - */ - ChannelOperations transformWrite(AllocatingTransformer transformer); - - /** - * Flushes any pending writes on this connection by calling {@link Channel#flush()}. This can be used for - * implementing any custom flusing strategies that otherwise can not be implemented by methods like - * {@link #write(Observable, Func1)}. - */ - void flush(); - - /** - * Flushes any pending writes and closes the connection. Same as calling {@code close(true)} - * - * @return {@link Observable} representing the result of close. - */ - Observable close(); - - /** - * Closes this channel after flushing all pending writes. - * - * @return {@link Observable} representing the result of close and flush. - */ - Observable close(boolean flush); - - /** - * Closes the connection immediately. Same as calling {@link #close()} and subscribing to the returned - * {@code Observable} - */ - void closeNow(); - - /** - * Returns an {@link Observable} that completes when this connection is closed. - * - * @return An {@link Observable} that completes when this connection is closed. - */ - Observable closeListener(); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ChannelSubscriberEvent.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/ChannelSubscriberEvent.java deleted file mode 100644 index 8a2e104..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ChannelSubscriberEvent.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.Channel; -import rx.Subscriber; - -/** - * An event to communicate the subscriber of a new channel created by {@link AbstractConnectionToChannelBridge}. - * - *

Connection reuse

- * - * For cases, where the {@link Connection} is pooled, reuse should be indicated explicitly via - * {@link ConnectionInputSubscriberResetEvent}. There can be multiple {@link ConnectionInputSubscriberResetEvent}s - * sent to the same channel and hence the same instance of {@link AbstractConnectionToChannelBridge}. - * - * @param Type read from the connection held by the event. - * @param Type written to the connection held by the event. - */ -public class ChannelSubscriberEvent { - - private final Subscriber subscriber; - - public ChannelSubscriberEvent(Subscriber subscriber) { - this.subscriber = subscriber; - } - - public Subscriber getSubscriber() { - return subscriber; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/Connection.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/Connection.java deleted file mode 100644 index 086ee69..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/Connection.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.util.AttributeKey; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.EventExecutorGroup; -import rx.Observable; -import rx.Observable.Transformer; -import rx.functions.Action1; -import rx.functions.Func1; - -/** - * An abstraction over netty's channel providing Rx APIs. - * - *

Reading data

- * - * Unless, {@link ChannelOption#AUTO_READ} is set to {@code true} on the underneath channel, data will be read from the - * connection if and only if there is a subscription to the input stream returned by {@link #getInput()}. - * In case, the input data is not required to be consumed, one should call {@link #ignoreInput()}, otherwise, data will - * never be read from the channel. - * - * @param Type of object that is read from this connection. - * @param Type of object that is written to this connection. - */ -public abstract class Connection implements ChannelOperations { - - public static final AttributeKey CONNECTION_ATTRIBUTE_KEY = AttributeKey.valueOf("rx-netty-conn-attr"); - - private final Channel nettyChannel; - private final ContentSource contentSource; - protected final MarkAwarePipeline markAwarePipeline; - - protected Connection(final Channel nettyChannel) { - if (null == nettyChannel) { - throw new IllegalArgumentException("Channel can not be null"); - } - this.nettyChannel = nettyChannel; - markAwarePipeline = new MarkAwarePipeline(nettyChannel.pipeline()); - contentSource = new ContentSource<>(nettyChannel, ConnectionInputSubscriberEvent::new); - } - - protected Connection(Connection toCopy) { - nettyChannel = toCopy.nettyChannel; - markAwarePipeline = toCopy.markAwarePipeline; - contentSource = toCopy.contentSource; - } - - protected Connection(Connection toCopy, ContentSource contentSource) { - nettyChannel = toCopy.nettyChannel; - markAwarePipeline = toCopy.markAwarePipeline; - this.contentSource = contentSource; - } - - /** - * Returns a stream of data that is read from the connection. - * - * Unless, {@link ChannelOption#AUTO_READ} is set to {@code true}, the content will only be read from the - * underneath channel, if there is a subscriber to the input. - * In case, input is not required to be read, call {@link #ignoreInput()} - * - * @return The stream of data that is read from the connection. - */ - public ContentSource getInput() { - return contentSource; - } - - /** - * Ignores all input on this connection. - * - * Unless, {@link ChannelOption#AUTO_READ} is set to {@code true}, the content will only be read from the - * underneath channel, if there is a subscriber to the input. So, upon recieving this connection, either one should - * call this method or eventually subscribe to the stream returned by {@link #getInput()} - * - * @return An {@link Observable}, subscription to which will discard the input. This {@code Observable} will - * error/complete when the input errors/completes and unsubscription from here will unsubscribe from the content. - */ - public Observable ignoreInput() { - return getInput().map(new Func1() { - @Override - public Void call(R r) { - ReferenceCountUtil.release(r); - return null; - } - }).ignoreElements(); - } - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for this connection. The specified handler is added at - * the first position of the pipeline as specified by {@link ChannelPipeline#addFirst(String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param name Name of the handler. - * @param handler Handler instance to add. - * - * @return {@code this}. - */ - public abstract Connection addChannelHandlerFirst(String name, ChannelHandler handler); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for this connection. The specified handler is added at - * the first position of the pipeline as specified by - * {@link ChannelPipeline#addFirst(EventExecutorGroup, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} methods - * @param name the name of the handler to append - * @param handler Handler instance to add. - * - * @return {@code this}. - */ - public abstract Connection addChannelHandlerFirst(EventExecutorGroup group, String name, - ChannelHandler handler); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for this connection. The specified handler is added at - * the last position of the pipeline as specified by {@link ChannelPipeline#addLast(String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param name Name of the handler. - * @param handler Handler instance to add. - * - * @return {@code this}. - */ - public abstract Connection addChannelHandlerLast(String name, ChannelHandler handler); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for this connection. The specified handler is added at - * the last position of the pipeline as specified by - * {@link ChannelPipeline#addLast(EventExecutorGroup, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} methods - * @param name the name of the handler to append - * @param handler Handler instance to add. - * - * @return {@code this}. - */ - public abstract Connection addChannelHandlerLast(EventExecutorGroup group, String name, - ChannelHandler handler); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for this connection. The specified - * handler is added before an existing handler with the passed {@code baseName} in the pipeline as specified by - * {@link ChannelPipeline#addBefore(String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param baseName the name of the existing handler - * @param name Name of the handler. - * @param handler Handler instance to add. - * - * @return {@code this}. - */ - public abstract Connection addChannelHandlerBefore(String baseName, String name, - ChannelHandler handler); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for this connection. The specified - * handler is added before an existing handler with the passed {@code baseName} in the pipeline as specified by - * {@link ChannelPipeline#addBefore(EventExecutorGroup, String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} - * methods - * @param baseName the name of the existing handler - * @param name the name of the handler to append - * @param handler Handler instance to add. - * - * @return {@code this}. - */ - public abstract Connection addChannelHandlerBefore(EventExecutorGroup group, String baseName, - String name, ChannelHandler handler); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for this connection. The specified - * handler is added after an existing handler with the passed {@code baseName} in the pipeline as specified by - * {@link ChannelPipeline#addAfter(String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param baseName the name of the existing handler - * @param name Name of the handler. - * @param handler Handler instance to add. - * - * @return {@code this}. - */ - public abstract Connection addChannelHandlerAfter(String baseName, String name, - ChannelHandler handler); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for this connection. The specified - * handler is added after an existing handler with the passed {@code baseName} in the pipeline as specified by - * {@link ChannelPipeline#addAfter(EventExecutorGroup, String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} methods - * @param baseName the name of the existing handler - * @param name the name of the handler to append - * @param handler Handler instance to add. - * - * @return {@code this}. - */ - public abstract Connection addChannelHandlerAfter(EventExecutorGroup group, String baseName, - String name, ChannelHandler handler); - - /** - * Configures the {@link ChannelPipeline} for this channel, using the passed {@code pipelineConfigurator}. - * - * @param pipelineConfigurator Action to configure {@link ChannelPipeline}. - * - * @return {@code this}. - */ - public abstract Connection pipelineConfigurator(Action1 pipelineConfigurator); - - /** - * Transforms this connection's input stream using the passed {@code transformer} to create a new - * {@code Connection} instance. - * - * @param transformer Transformer to transform the input stream. - * - * @param New type of the input stream. - * - * @return A new connection instance with the transformed read stream. - */ - public abstract Connection transformRead(Transformer transformer); - - /** - * Transforms this connection to enable writing a different object type. - * - * @param transformer Transformer to transform objects written to the channel. - * - * @param New object types to be written to the connection. - * - * @return A new connection instance with the new write type. - */ - public abstract Connection transformWrite(AllocatingTransformer transformer); - - /** - * Returns the {@link MarkAwarePipeline} for this connection, changes to which can be reverted at any point in time. - */ - public MarkAwarePipeline getResettableChannelPipeline() { - return markAwarePipeline; - } - - /** - * Returns the {@link ChannelPipeline} for this connection. - * - * @return {@link ChannelPipeline} for this connection. - */ - public ChannelPipeline getChannelPipeline() { - return nettyChannel.pipeline(); - } - - /** - * Returns the underlying netty {@link Channel} for this connection. - * - * It is advisable to use this connection abstraction for all interactions with the channel, however, advanced users - * may find directly using the netty channel useful in some cases. - * - * @return The underlying netty {@link Channel} for this connection. - */ - public Channel unsafeNettyChannel() { - return nettyChannel; - } - - /* - * In order to make sure that the connection is correctly initialized, the listener needs to be added post - * constructor. Otherwise, there is a race-condition of the channel closed before the connection is completely - * created and the Connection.close() call on channel close can access the Connection object which isn't - * constructed completely. IOW, "this" escapes from the constructor if the listener is added in the constructor. - */ - protected void connectCloseToChannelClose() { - nettyChannel.closeFuture() - .addListener((ChannelFutureListener) future -> { - closeNow(); // Close this connection when the channel is closed. - }); - nettyChannel.attr(CONNECTION_ATTRIBUTE_KEY).set(this); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionCreationFailedEvent.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionCreationFailedEvent.java deleted file mode 100644 index afb93b6..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionCreationFailedEvent.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandler; - -/** - * An event to indicate to {@link AbstractConnectionToChannelBridge} that the subscriber as published by - * {@link ChannelSubscriberEvent} should be informed of a connection creation failure, instead of a new connection. - * - *

Why do we need this?

- * - * Since, emitting a connection can include a handshake for protocols such as TLS/SSL, it is not so that a new - * {@link io.reactivex.netty.channel.Connection} should be emitted as soon as the channel is active (i.e. inside - * {@link ChannelInboundHandler#channelActive(ChannelHandlerContext)}). - * For this reason, this event leaves it to the pipeline or any other entity outside to decide, when is the rite time to - * determine that a connection for a channel has failed creation. - */ -public final class ConnectionCreationFailedEvent { - - private final Throwable throwable; - - public ConnectionCreationFailedEvent(Throwable throwable) { - this.throwable = throwable; - } - - public Throwable getThrowable() { - return throwable; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionImpl.java deleted file mode 100644 index 30126ff..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionImpl.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.FileRegion; -import io.netty.util.concurrent.EventExecutorGroup; -import io.reactivex.netty.channel.events.ConnectionEventListener; -import io.reactivex.netty.events.EventAttributeKeys; -import io.reactivex.netty.events.EventPublisher; -import rx.Observable; -import rx.Observable.Transformer; -import rx.functions.Action1; -import rx.functions.Func1; - -/** - * An implementation of {@link Connection} delegating all {@link ChannelOperations} methods to - * {@link DefaultChannelOperations}. - */ -public final class ConnectionImpl extends Connection { - - private final ChannelOperations delegate; - - private ConnectionImpl(Channel nettyChannel, ConnectionEventListener eventListener, EventPublisher eventPublisher) { - super(nettyChannel); - delegate = new DefaultChannelOperations<>(nettyChannel, eventListener, eventPublisher); - } - - private ConnectionImpl(Channel nettyChannel, ChannelOperations delegate) { - super(nettyChannel); - this.delegate = delegate; - } - - private ConnectionImpl(ConnectionImpl toCopy, ContentSource contentSource, ChannelOperations delegate) { - super(toCopy, contentSource); - this.delegate = delegate; - } - - @Override - public Observable write(Observable msgs) { - return delegate.write(msgs); - } - - @Override - public Observable write(Observable msgs, Func1 flushSelector) { - return delegate.write(msgs, flushSelector); - } - - @Override - public Observable writeAndFlushOnEach(Observable msgs) { - return delegate.writeAndFlushOnEach(msgs); - } - - @Override - public Observable writeString(Observable msgs) { - return delegate.writeString(msgs); - } - - @Override - public Observable writeString(Observable msgs, Func1 flushSelector) { - return delegate.writeString(msgs, flushSelector); - } - - @Override - public Observable writeStringAndFlushOnEach(Observable msgs) { - return delegate.writeStringAndFlushOnEach(msgs); - } - - @Override - public Observable writeBytes(Observable msgs) { - return delegate.writeBytes(msgs); - } - - @Override - public Observable writeBytes(Observable msgs, - Func1 flushSelector) { - return delegate.writeBytes(msgs, flushSelector); - } - - @Override - public Observable writeBytesAndFlushOnEach(Observable msgs) { - return delegate.writeBytesAndFlushOnEach(msgs); - } - - @Override - public Observable writeFileRegion(Observable msgs) { - return delegate.writeFileRegion(msgs); - } - - @Override - public Observable writeFileRegion(Observable msgs, - Func1 flushSelector) { - return delegate.writeFileRegion(msgs, flushSelector); - } - - @Override - public Observable writeFileRegionAndFlushOnEach(Observable msgs) { - return delegate.writeFileRegionAndFlushOnEach(msgs); - } - - @Override - public void flush() { - delegate.flush(); - } - - @Override - public Observable close() { - return delegate.close(); - } - - @Override - public Observable close(boolean flush) { - return delegate.close(flush); - } - - @Override - public void closeNow() { - delegate.closeNow(); - } - - @Override - public Observable closeListener() { - return delegate.closeListener(); - } - - public static ConnectionImpl fromChannel(Channel nettyChannel) { - EventPublisher ep = nettyChannel.attr(EventAttributeKeys.EVENT_PUBLISHER).get(); - if (null == ep) { - throw new IllegalArgumentException("No event publisher set in the channel."); - } - - ConnectionEventListener l = null; - if (ep.publishingEnabled()) { - l = nettyChannel.attr(EventAttributeKeys.CONNECTION_EVENT_LISTENER).get(); - if (null == l) { - throw new IllegalArgumentException("No event listener set in the channel."); - } - } - - final ConnectionImpl toReturn = new ConnectionImpl<>(nettyChannel, l, ep); - toReturn.connectCloseToChannelClose(); - return toReturn; - } - - /*Visible for testing*/static ConnectionImpl create(Channel nettyChannel, - ChannelOperations delegate) { - final ConnectionImpl toReturn = new ConnectionImpl<>(nettyChannel, delegate); - toReturn.connectCloseToChannelClose(); - return toReturn; - } - - @Override - public Connection addChannelHandlerFirst(String name, ChannelHandler handler) { - getResettableChannelPipeline().markIfNotYetMarked().addFirst(name, handler); - return cast(); - } - - @Override - public Connection addChannelHandlerFirst(EventExecutorGroup group, String name, - ChannelHandler handler) { - getResettableChannelPipeline().markIfNotYetMarked().addFirst(group, name, handler); - return cast(); - } - - @Override - public Connection addChannelHandlerLast(String name, ChannelHandler handler) { - getResettableChannelPipeline().markIfNotYetMarked().addLast(name, handler); - return cast(); - } - - @Override - public Connection addChannelHandlerLast(EventExecutorGroup group, String name, - ChannelHandler handler) { - getResettableChannelPipeline().markIfNotYetMarked().addLast(group, name, handler); - return cast(); - } - - @Override - public Connection addChannelHandlerBefore(String baseName, String name, ChannelHandler handler) { - getResettableChannelPipeline().markIfNotYetMarked().addBefore(baseName, name, handler); - return cast(); - } - - @Override - public Connection addChannelHandlerBefore(EventExecutorGroup group, String baseName, String name, - ChannelHandler handler) { - getResettableChannelPipeline().markIfNotYetMarked().addBefore(group, baseName, name, handler); - return cast(); - } - - @Override - public Connection addChannelHandlerAfter(String baseName, String name, ChannelHandler handler) { - getResettableChannelPipeline().markIfNotYetMarked().addAfter(baseName, name, handler); - return cast(); - } - - @Override - public Connection addChannelHandlerAfter(EventExecutorGroup group, String baseName, String name, - ChannelHandler handler) { - getResettableChannelPipeline().markIfNotYetMarked().addAfter(group, baseName, name, handler); - return cast(); - } - - @Override - public Connection pipelineConfigurator(Action1 pipelineConfigurator) { - pipelineConfigurator.call(getResettableChannelPipeline().markIfNotYetMarked()); - return cast(); - } - - @Override - public Connection transformRead(Transformer transformer) { - return new ConnectionImpl<>(this, getInput().transform(transformer), delegate); - } - - @Override - public Connection transformWrite(AllocatingTransformer transformer) { - return new ConnectionImpl<>(this, getInput(), delegate.transformWrite(transformer)); - } - - @SuppressWarnings("unchecked") - protected Connection cast() { - return (Connection) this; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionInputSubscriberEvent.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionInputSubscriberEvent.java deleted file mode 100644 index 5bdd87c..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionInputSubscriberEvent.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.util.ReferenceCountUtil; -import rx.Subscriber; -import rx.functions.Action1; -import rx.observers.Subscribers; - -/** - * An event to communicate the subscriber of the associated connection input stream created by - * {@link AbstractConnectionToChannelBridge}. - * - *

Multiple events on the same channel

- * - * Multiple instance of this event can be sent on the same channel, provided that there is a - * {@link ConnectionInputSubscriberResetEvent} between two consecutive {@link ConnectionInputSubscriberEvent}s - * - * @param Type read from the connection held by the event. - * @param Type written to the connection held by the event. - */ -public final class ConnectionInputSubscriberEvent { - - private final Subscriber subscriber; - - public ConnectionInputSubscriberEvent(Subscriber subscriber) { - if (null == subscriber) { - throw new NullPointerException("Subscriber can not be null"); - } - this.subscriber = subscriber; - } - - public Subscriber getSubscriber() { - return subscriber; - } - - public static ConnectionInputSubscriberEvent discardAllInput() { - return new ConnectionInputSubscriberEvent<>(Subscribers.create(new Action1() { - @Override - public void call(II msg) { - ReferenceCountUtil.release(msg); - } - }, new Action1() { - @Override - public void call(Throwable throwable) { - // Empty as we are discarding input anyways. - } - })); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionInputSubscriberReplaceEvent.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionInputSubscriberReplaceEvent.java deleted file mode 100644 index 31ee184..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionInputSubscriberReplaceEvent.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -/** - * This event is an indication to atomically replace existing connection input subscriber, if any, with another. - */ -public class ConnectionInputSubscriberReplaceEvent { - - private final ConnectionInputSubscriberEvent newSubEvent; - - public ConnectionInputSubscriberReplaceEvent(ConnectionInputSubscriberEvent newSubEvent) { - this.newSubEvent = newSubEvent; - } - - public ConnectionInputSubscriberEvent getNewSubEvent() { - return newSubEvent; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionInputSubscriberResetEvent.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionInputSubscriberResetEvent.java deleted file mode 100644 index dd1227b..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionInputSubscriberResetEvent.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -/** - * This event is an indication that there will be multiple subscribers to the connection input stream. This event - * must be sent as many times as the subscribers to the input. This typically will be the case for client-side - * connections when a channel is pooled and reused. - */ -public interface ConnectionInputSubscriberResetEvent { - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionSubscriberEvent.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionSubscriberEvent.java deleted file mode 100644 index 205c9e8..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ConnectionSubscriberEvent.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import rx.Subscriber; - -/** - * An event to communicate the subscriber of a new connection created by {@link AbstractConnectionToChannelBridge}. - * - *

Connection reuse

- * - * For cases, where the {@link Connection} is pooled, reuse should be indicated explicitly via - * {@link ConnectionInputSubscriberResetEvent}. There can be multiple {@link ConnectionInputSubscriberResetEvent}s - * sent to the same channel and hence the same instance of {@link AbstractConnectionToChannelBridge}. - * - * @param Type read from the connection held by the event. - * @param Type written to the connection held by the event. - */ -public class ConnectionSubscriberEvent { - - private final Subscriber> subscriber; - - public ConnectionSubscriberEvent(Subscriber> subscriber) { - this.subscriber = subscriber; - } - - public Subscriber> getSubscriber() { - return subscriber; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ContentSource.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/ContentSource.java deleted file mode 100644 index 73d0122..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/ContentSource.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.channel.Channel; -import rx.Observable; -import rx.Subscriber; -import rx.functions.Func1; - -/** - * A source for any content/data read from a channel. - * - *

Managing {@link ByteBuf} lifecycle.

- * - * If this source emits {@link ByteBuf} or a {@link ByteBufHolder}, using {@link #autoRelease()} will release the buffer - * after emitting it from this source. - * - *

Replaying content

- * - * Since, the content read from a channel is not re-readable, this also provides a {@link #replayable()} function that - * produces a source which can be subscribed multiple times to replay the same data. This is specially useful if the - * content read from one channel is written on to another with an option to retry. - * - * @param - */ -public final class ContentSource extends Observable { - - private ContentSource(final Observable source) { - super(new OnSubscribe() { - @Override - public void call(Subscriber subscriber) { - source.unsafeSubscribe(subscriber); - } - }); - } - - public ContentSource(final Channel channel, final Func1, Object> subscriptionEventFactory) { - super(new OnSubscribe() { - @Override - public void call(Subscriber subscriber) { - channel.pipeline() - .fireUserEventTriggered(subscriptionEventFactory.call(subscriber)); - } - }); - } - - public ContentSource(final Throwable error) { - super(new OnSubscribe() { - @Override - public void call(Subscriber subscriber) { - subscriber.onError(error); - } - }); - } - - /** - * If this source emits {@link ByteBuf} or {@link ByteBufHolder} then using this operator will release the buffer - * after it is emitted from this source. - * - * @return A new instance of the stream with auto-release enabled. - */ - public Observable autoRelease() { - return this.lift(new AutoReleaseOperator()); - } - - /** - * This provides a replayable content source that only subscribes once to the actual content and then caches it, - * till {@link DisposableContentSource#dispose()} is called. - * - * @return A new replayable content source. - */ - public DisposableContentSource replayable() { - return DisposableContentSource.createNew(this); - } - - public ContentSource transform(Transformer transformer) { - return new ContentSource<>(transformer.call(this)); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/DefaultChannelOperations.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/DefaultChannelOperations.java deleted file mode 100644 index e65aeab..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/DefaultChannelOperations.java +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.FileRegion; -import io.reactivex.netty.channel.events.ConnectionEventListener; -import io.reactivex.netty.events.Clock; -import io.reactivex.netty.events.EventPublisher; -import java.util.logging.Level; -import java.util.logging.Logger; -import rx.Observable; -import rx.Observable.OnSubscribe; -import rx.Subscriber; -import rx.functions.Action0; -import rx.functions.Action1; -import rx.functions.Actions; -import rx.functions.Func1; -import rx.subscriptions.Subscriptions; - -import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; - -import static java.util.concurrent.TimeUnit.*; - -/** - * Default implementation for {@link ChannelOperations}. - * - * @param Type of data that can be written on the associated channel. - */ -public class DefaultChannelOperations implements ChannelOperations { - - private static final Logger logger = Logger.getLogger(DefaultChannelOperations.class.getName()); - - /** Field updater for closeIssued. */ - @SuppressWarnings("rawtypes") - private static final AtomicIntegerFieldUpdater CLOSE_ISSUED_UPDATER - = AtomicIntegerFieldUpdater.newUpdater(DefaultChannelOperations.class, "closeIssued"); - @SuppressWarnings("unused") - private volatile int closeIssued; // updated by the atomic updater, so required to be volatile. - - private final Channel nettyChannel; - private final ConnectionEventListener eventListener; - private final EventPublisher eventPublisher; - - private final Observable closeObservable; - private final Observable flushAndCloseObservable; - - private final Func1 flushOnEachSelector = new Func1() { - @Override - public Boolean call(W w) { - return true; - } - }; - - public DefaultChannelOperations(final Channel nettyChannel, ConnectionEventListener eventListener, - EventPublisher eventPublisher) { - this.nettyChannel = nettyChannel; - this.eventListener = eventListener; - this.eventPublisher = eventPublisher; - closeObservable = Observable.create(new OnSubscribeForClose(nettyChannel)); - flushAndCloseObservable = closeObservable.doOnSubscribe(new Action0() { - @Override - public void call() { - flush(); - } - }); - } - - @Override - public Observable write(final Observable msgs) { - return _write(msgs); - } - - @Override - public Observable write(Observable msgs, final Func1 flushSelector) { - return _write(msgs, flushSelector); - } - - @Override - public Observable writeAndFlushOnEach(Observable msgs) { - return _write(msgs, flushOnEachSelector); - } - - @Override - public Observable writeString(Observable msgs) { - return _write(msgs); - } - - @Override - public Observable writeString(Observable msgs, Func1 flushSelector) { - return _write(msgs, flushSelector); - } - - @Override - public Observable writeStringAndFlushOnEach(Observable msgs) { - return writeString(msgs, FLUSH_ON_EACH_STRING); - } - - @Override - public Observable writeBytes(Observable msgs) { - return _write(msgs); - } - - @Override - public Observable writeBytes(Observable msgs, Func1 flushSelector) { - return _write(msgs, flushSelector); - } - - @Override - public Observable writeBytesAndFlushOnEach(Observable msgs) { - return _write(msgs, FLUSH_ON_EACH_BYTES); - } - - @Override - public Observable writeFileRegion(Observable msgs) { - return _write(msgs); - } - - @Override - public Observable writeFileRegion(Observable msgs, Func1 flushSelector) { - return _write(msgs, flushSelector); - } - - @Override - public Observable writeFileRegionAndFlushOnEach(Observable msgs) { - return writeFileRegion(msgs, FLUSH_ON_EACH_FILE_REGION); - } - - @Override - public ChannelOperations transformWrite(AllocatingTransformer transformer) { - nettyChannel.pipeline().fireUserEventTriggered(new AppendTransformerEvent<>(transformer)); - return new DefaultChannelOperations<>(nettyChannel, eventListener, eventPublisher); - } - - @Override - public void flush() { - if (eventPublisher.publishingEnabled()) { - final long startTimeNanos = Clock.newStartTimeNanos(); - eventListener.onFlushStart(); - if (nettyChannel.eventLoop().inEventLoop()) { - _flushInEventloop(startTimeNanos); - } else { - nettyChannel.eventLoop() - .execute(new Runnable() { - @Override - public void run() { - _flushInEventloop(startTimeNanos); - } - }); - } - } else { - nettyChannel.flush(); - } - } - - @Override - public Observable close() { - return close(true); - } - - @Override - public Observable close(boolean flush) { - return flush ? flushAndCloseObservable : closeObservable; - } - - @Override - public void closeNow() { - close().subscribe(Actions.empty(), new Action1() { - @Override - public void call(Throwable throwable) { - logger.log(Level.SEVERE, "Error closing connection.", throwable); - } - }); - } - - @Override - public Observable closeListener() { - return Observable.create(new OnSubscribe() { - @Override - public void call(final Subscriber subscriber) { - final SubscriberToChannelFutureBridge l = new SubscriberToChannelFutureBridge() { - - @Override - protected void doOnSuccess(ChannelFuture future) { - subscriber.onCompleted(); - } - - @Override - protected void doOnFailure(ChannelFuture future, Throwable cause) { - subscriber.onCompleted(); - } - }; - l.bridge(nettyChannel.closeFuture(), subscriber); - } - }); - } - - private Observable _write(final Observable msgs, Func1 flushSelector) { - return _write(msgs.lift(new FlushSelectorOperator<>(flushSelector, this))); - } - - private void _flushInEventloop(long startTimeNanos) { - assert nettyChannel.eventLoop().inEventLoop(); - nettyChannel.flush(); // Flush is sync when from eventloop. - eventListener.onFlushComplete(Clock.onEndNanos(startTimeNanos), NANOSECONDS); - } - - private Observable _write(final Observable msgs) { - return Observable.create(new OnSubscribe() { - @Override - public void call(final Subscriber subscriber) { - - final long startTimeNanos = Clock.newStartTimeNanos(); - - if (eventPublisher.publishingEnabled()) { - eventListener.onWriteStart(); - } - - /* - * If a write happens from outside the eventloop, it does not wakeup the selector, till a flush happens. - * In absence of a selector wakeup, this write will be delayed by the selector sleep interval. - * The code below makes sure that the selector is woken up on a write (by executing a task that does - * the write) - */ - if (nettyChannel.eventLoop().inEventLoop()) { - _writeStreamToChannel(subscriber, startTimeNanos); - } else { - nettyChannel.eventLoop() - .execute(new Runnable() { - @Override - public void run() { - _writeStreamToChannel(subscriber, startTimeNanos); - } - }); - } - } - - private void _writeStreamToChannel(final Subscriber subscriber, final long startTimeNanos) { - final ChannelFuture writeFuture = nettyChannel.write(msgs.doOnCompleted(new Action0() { - @Override - public void call() { - Boolean shdNotFlush = nettyChannel.attr(FLUSH_ONLY_ON_READ_COMPLETE).get(); - if (null == shdNotFlush || !shdNotFlush) { - flush(); - } - } - })); - subscriber.add(Subscriptions.create(new Action0() { - @Override - public void call() { - writeFuture.cancel(false); // cancel write on unsubscribe. - } - })); - writeFuture.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (subscriber.isUnsubscribed()) { - /*short-circuit if subscriber is unsubscribed*/ - return; - } - - if (future.isSuccess()) { - if (eventPublisher.publishingEnabled()) { - eventListener.onWriteSuccess(Clock.onEndNanos(startTimeNanos), NANOSECONDS); - } - subscriber.onCompleted(); - } else { - if (eventPublisher.publishingEnabled()) { - eventListener.onWriteFailed(Clock.onEndNanos(startTimeNanos), NANOSECONDS, - future.cause()); - } - subscriber.onError(future.cause()); - } - } - }); - } - }); - } - - private class OnSubscribeForClose implements OnSubscribe { - - private final Channel nettyChannel; - - public OnSubscribeForClose(Channel nettyChannel) { - this.nettyChannel = nettyChannel; - } - - @Override - @SuppressWarnings("unchecked") - public void call(final Subscriber subscriber) { - - final long closeStartTimeNanos = Clock.newStartTimeNanos(); - - final ChannelCloseListener closeListener; - if (CLOSE_ISSUED_UPDATER.compareAndSet(DefaultChannelOperations.this, 0, 1)) { - if (eventPublisher.publishingEnabled()) { - eventListener.onConnectionCloseStart(); - } - - nettyChannel.close(); // close only once. - - closeListener = new ChannelCloseListener(eventListener, eventPublisher, closeStartTimeNanos, - subscriber); - } else { - closeListener = new ChannelCloseListener(subscriber); - } - - closeListener.bridge(nettyChannel.closeFuture(), subscriber); - } - - private class ChannelCloseListener extends SubscriberToChannelFutureBridge { - - private final long closeStartTimeNanos; - private final Subscriber subscriber; - private final ConnectionEventListener eventListener; - private final EventPublisher eventPublisher; - - public ChannelCloseListener(ConnectionEventListener eventListener, EventPublisher eventPublisher, - long closeStartTimeNanos, Subscriber subscriber) { - this.eventListener = eventListener; - this.eventPublisher = eventPublisher; - this.closeStartTimeNanos = closeStartTimeNanos; - this.subscriber = subscriber; - } - - public ChannelCloseListener(Subscriber subscriber) { - this(null, null, -1, subscriber); - } - - @Override - protected void doOnSuccess(ChannelFuture future) { - if (null != eventListener && eventPublisher.publishingEnabled()) { - eventListener.onConnectionCloseSuccess(Clock.onEndNanos(closeStartTimeNanos), NANOSECONDS); - } - if (!subscriber.isUnsubscribed()) { - subscriber.onCompleted(); - } - } - - @Override - protected void doOnFailure(ChannelFuture future, Throwable cause) { - if (null != eventListener && eventPublisher.publishingEnabled()) { - eventListener.onConnectionCloseFailed(Clock.onEndNanos(closeStartTimeNanos), NANOSECONDS, - future.cause()); - } - if (!subscriber.isUnsubscribed()) { - subscriber.onError(future.cause()); - } - } - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/DetachedChannelPipeline.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/DetachedChannelPipeline.java deleted file mode 100644 index 7851602..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/DetachedChannelPipeline.java +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.util.concurrent.EventExecutorGroup; -import java.util.logging.Level; -import java.util.logging.Logger; -import rx.functions.Action1; -import rx.functions.Func0; - -import java.util.LinkedList; -import java.util.Map.Entry; -import java.util.NoSuchElementException; - -/** - * An implementation of {@link ChannelPipeline} which is detached from a channel and provides a - * {@link #addToChannel(Channel)} method to be invoked when this pipeline handlers are to be added to an actual channel - * pipeline. - * - * This must NOT be used on an actual channel, it does not support any channel operations. It only supports pipeline - * modification operations. - */ -public class DetachedChannelPipeline { - - private static final Logger logger = Logger.getLogger(DetachedChannelPipeline.class.getName()); - - private final LinkedList holdersInOrder; - - private final Action1 nullableTail; - - public DetachedChannelPipeline() { - this(null); - } - - public DetachedChannelPipeline(final Action1 nullableTail) { - this.nullableTail = nullableTail; - holdersInOrder = new LinkedList<>(); - } - - private DetachedChannelPipeline(final DetachedChannelPipeline copyFrom, - final Action1 nullableTail) { - this.nullableTail = nullableTail; - holdersInOrder = new LinkedList<>(); - synchronized (copyFrom.holdersInOrder) { - for (HandlerHolder handlerHolder : copyFrom.holdersInOrder) { - holdersInOrder.addLast(handlerHolder); - } - } - } - - public ChannelInitializer getChannelInitializer() { - return new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - final ChannelPipeline pipeline = ch.pipeline(); - synchronized (holdersInOrder) { - unguardedCopyToPipeline(pipeline); - } - } - }; - } - - public void addToChannel(Channel channel) { - final ChannelPipeline pipeline = channel.pipeline(); - synchronized (holdersInOrder) { - unguardedCopyToPipeline(pipeline); - } - } - - public DetachedChannelPipeline copy() { - return copy(null); - } - - public DetachedChannelPipeline copy(Action1 newTail) { - return new DetachedChannelPipeline(this, newTail); - } - - public DetachedChannelPipeline addFirst(String name, Func0 handlerFactory) { - return _guardedAddFirst(new HandlerHolder(name, handlerFactory)); - } - - public DetachedChannelPipeline addFirst(EventExecutorGroup group, - String name, Func0 handlerFactory) { - return _guardedAddFirst(new HandlerHolder(name, handlerFactory, group)); - } - - public DetachedChannelPipeline addLast(String name, Func0 handlerFactory) { - return _guardedAddLast(new HandlerHolder(name, handlerFactory)); - } - - public DetachedChannelPipeline addLast(EventExecutorGroup group, String name, Func0 handlerFactory) { - return _guardedAddLast(new HandlerHolder(name, handlerFactory, group)); - } - - public DetachedChannelPipeline addBefore(String baseName, String name, Func0 handlerFactory) { - return _guardedAddBefore(baseName, new HandlerHolder(name, handlerFactory)); - } - - public DetachedChannelPipeline addBefore(EventExecutorGroup group, String baseName, String name, Func0 handlerFactory) { - return _guardedAddBefore(baseName, new HandlerHolder(name, handlerFactory, group)); - } - - public DetachedChannelPipeline addAfter(String baseName, String name, Func0 handlerFactory) { - return _guardedAddAfter(baseName, new HandlerHolder(name, handlerFactory)); - } - - public DetachedChannelPipeline addAfter(EventExecutorGroup group, String baseName, String name, Func0 handlerFactory) { - return _guardedAddAfter(baseName, new HandlerHolder(name, handlerFactory, group)); - } - - @SafeVarargs - public final DetachedChannelPipeline addFirst(Func0... handlerFactories) { - synchronized (holdersInOrder) { - for (int i = handlerFactories.length - 1; i >= 0; i--) { - Func0 handlerFactory = handlerFactories[i]; - holdersInOrder.addFirst(new HandlerHolder(handlerFactory)); - } - } - return this; - } - - @SafeVarargs - public final DetachedChannelPipeline addFirst(EventExecutorGroup group, Func0... handlerFactories) { - synchronized (holdersInOrder) { - for (int i = handlerFactories.length - 1; i >= 0; i--) { - Func0 handlerFactory = handlerFactories[i]; - holdersInOrder.addFirst(new HandlerHolder(null, handlerFactory, group)); - } - } - return this; - } - - @SafeVarargs - public final DetachedChannelPipeline addLast(Func0... handlerFactories) { - for (Func0 handlerFactory : handlerFactories) { - _guardedAddLast(new HandlerHolder(handlerFactory)); - } - return this; - } - - @SafeVarargs - public final DetachedChannelPipeline addLast(EventExecutorGroup group, Func0... handlerFactories) { - for (Func0 handlerFactory : handlerFactories) { - _guardedAddLast(new HandlerHolder(null, handlerFactory, group)); - } - return this; - } - - public DetachedChannelPipeline configure(Action1 configurator) { - _guardedAddLast(new HandlerHolder(configurator)); - return this; - } - - public void copyTo(ChannelPipeline pipeline) { - synchronized (holdersInOrder) { - unguardedCopyToPipeline(pipeline); - } - } - - /*Visible for testing*/ LinkedList getHoldersInOrder() { - return holdersInOrder; - } - - private void unguardedCopyToPipeline(ChannelPipeline pipeline) { /*To be guarded by lock on holders*/ - for (HandlerHolder holder : holdersInOrder) { - if (holder.hasPipelineConfigurator()) { - holder.getPipelineConfigurator().call(pipeline); - continue; - } - - if (holder.hasGroup()) { - if (holder.hasName()) { - pipeline.addLast(holder.getGroupIfConfigured(), holder.getNameIfConfigured(), - holder.getHandlerFactoryIfConfigured().call()); - } else { - pipeline.addLast(holder.getGroupIfConfigured(), holder.getHandlerFactoryIfConfigured().call()); - } - } else if (holder.hasName()) { - pipeline.addLast(holder.getNameIfConfigured(), holder.getHandlerFactoryIfConfigured().call()); - } else { - pipeline.addLast(holder.getHandlerFactoryIfConfigured().call()); - } - } - - if (null != nullableTail) { - nullableTail.call(pipeline); // This is the last handler to be added to the pipeline always. - } - - if (logger.isLoggable(Level.FINE)) { - logger.log(Level.FINE, "Channel pipeline in initializer: " + pipelineToString(pipeline)); - } - } - - private HandlerHolder unguardedFindHandlerByName(String baseName, boolean leniant) { - for (HandlerHolder handlerHolder : holdersInOrder) { - if (handlerHolder.hasName() && handlerHolder.getNameIfConfigured().equals(baseName)) { - return handlerHolder; - } - } - if (leniant) { - return null; - } else { - throw new NoSuchElementException("No handler with name: " + baseName + " configured in the pipeline."); - } - } - - private DetachedChannelPipeline _guardedAddFirst(HandlerHolder toAdd) { - synchronized (holdersInOrder) { - holdersInOrder.addFirst(toAdd); - } - return this; - } - - private DetachedChannelPipeline _guardedAddLast(HandlerHolder toAdd) { - synchronized (holdersInOrder) { - holdersInOrder.addLast(toAdd); - } - return this; - } - - private DetachedChannelPipeline _guardedAddBefore(String baseName, HandlerHolder toAdd) { - synchronized (holdersInOrder) { - HandlerHolder before = unguardedFindHandlerByName(baseName, false); - final int indexOfBefore = holdersInOrder.indexOf(before); - holdersInOrder.add(indexOfBefore, toAdd); - } - return this; - } - - private DetachedChannelPipeline _guardedAddAfter(String baseName, HandlerHolder toAdd) { - synchronized (holdersInOrder) { - HandlerHolder after = unguardedFindHandlerByName(baseName, false); - final int indexOfAfter = holdersInOrder.indexOf(after); - holdersInOrder.add(indexOfAfter + 1, toAdd); - } - return this; - } - - private static String pipelineToString(ChannelPipeline pipeline) { - StringBuilder builder = new StringBuilder(); - for (Entry handlerEntry : pipeline) { - if (builder.length() == 0) { - builder.append("[\n"); - } else { - builder.append(" ==> "); - } - builder.append("{ name =>") - .append(handlerEntry.getKey()) - .append(", handler => ") - .append(handlerEntry.getValue()) - .append("}\n") - ; - } - - if (builder.length() > 0) { - builder.append("}\n"); - } - return builder.toString(); - } - - /** - * A holder class for holding handler information, required to add handlers to the actual pipeline. - */ - /*Visible for testing*/ static class HandlerHolder { - - private final String nameIfConfigured; - private final Func0 handlerFactoryIfConfigured; - private final Action1 pipelineConfigurator; - private final EventExecutorGroup groupIfConfigured; - - HandlerHolder(Action1 pipelineConfigurator) { - this.pipelineConfigurator = pipelineConfigurator; - nameIfConfigured = null; - handlerFactoryIfConfigured = null; - groupIfConfigured = null; - } - - HandlerHolder(Func0 handlerFactory) { - this(null, handlerFactory); - } - - HandlerHolder(String name, Func0 handlerFactory) { - this(name, handlerFactory, null); - } - - HandlerHolder(String name, Func0 handlerFactory, EventExecutorGroup group) { - nameIfConfigured = name; - handlerFactoryIfConfigured = handlerFactory; - groupIfConfigured = group; - pipelineConfigurator = null; - } - - public String getNameIfConfigured() { - return nameIfConfigured; - } - - public boolean hasName() { - return null != nameIfConfigured; - } - - public Func0 getHandlerFactoryIfConfigured() { - return handlerFactoryIfConfigured; - } - - public EventExecutorGroup getGroupIfConfigured() { - return groupIfConfigured; - } - - public boolean hasGroup() { - return null != groupIfConfigured; - } - - public Action1 getPipelineConfigurator() { - return pipelineConfigurator; - } - - public boolean hasPipelineConfigurator() { - return null != pipelineConfigurator; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof HandlerHolder)) { - return false; - } - - HandlerHolder that = (HandlerHolder) o; - - if (groupIfConfigured != null? !groupIfConfigured.equals(that.groupIfConfigured) : - that.groupIfConfigured != null) { - return false; - } - if (handlerFactoryIfConfigured != null? - !handlerFactoryIfConfigured.equals(that.handlerFactoryIfConfigured) : - that.handlerFactoryIfConfigured != null) { - return false; - } - if (nameIfConfigured != null? !nameIfConfigured.equals(that.nameIfConfigured) : - that.nameIfConfigured != null) { - return false; - } - if (pipelineConfigurator != null? !pipelineConfigurator.equals(that.pipelineConfigurator) : - that.pipelineConfigurator != null) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - int result = nameIfConfigured != null? nameIfConfigured.hashCode() : 0; - result = 31 * result + (handlerFactoryIfConfigured != null? handlerFactoryIfConfigured.hashCode() : 0); - result = 31 * result + (pipelineConfigurator != null? pipelineConfigurator.hashCode() : 0); - result = 31 * result + (groupIfConfigured != null? groupIfConfigured.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "HandlerHolder{" + "nameIfConfigured='" + nameIfConfigured + '\'' + ", handlerFactoryIfConfigured=" + - handlerFactoryIfConfigured + ", pipelineConfigurator=" + pipelineConfigurator - + ", groupIfConfigured=" + groupIfConfigured + '}'; - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/DisposableContentSource.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/DisposableContentSource.java deleted file mode 100644 index f698256..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/DisposableContentSource.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.util.ReferenceCountUtil; -import rx.Observable; -import rx.Subscriber; -import rx.functions.Action1; -import rx.observables.ConnectableObservable; - -import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Similar to {@link ContentSource} but supports multicast to multiple subscriptions. This source, subscribes upstream - * once and then caches the content, till the time {@link #dispose()} is called. - * - *

Managing {@link ByteBuf} lifecycle.

- * - * If this source emits {@link ByteBuf} or a {@link ByteBufHolder}, using {@link #autoRelease()} will release the buffer - * after emitting it from this source. - * - * Every subscriber to this source must manage it's own lifecycle of the items it receives i.e. the buffers must be - * released by every subscriber post processing. - * - *

Disposing the source

- * - * It is mandatory to call {@link #dispose()} on this source when no more subscriptions are required. Failure to do so, - * will cause a buffer leak as this source, caches the contents till disposed. - * - * Typically, {@link #dispose()} can be called as an {@link Subscriber#unsubscribe()} action. - * - * @param Type of objects emitted by this source. - */ -public final class DisposableContentSource extends Observable { - - private final OnSubscribeImpl onSubscribe; - - private DisposableContentSource(final OnSubscribeImpl onSubscribe) { - super(onSubscribe); - this.onSubscribe = onSubscribe; - } - - /** - * If this source emits {@link ByteBuf} or {@link ByteBufHolder} then using this operator will release the buffer - * after it is emitted from this source. - * - * @return A new instance of the stream with auto-release enabled. - */ - public Observable autoRelease() { - return this.lift(new AutoReleaseOperator()); - } - - /** - * Disposes this source. - */ - public void dispose() { - if (onSubscribe.disposed.compareAndSet(false, true)) { - for (Object chunk : onSubscribe.chunks) { - ReferenceCountUtil.release(chunk); - } - onSubscribe.chunks.clear(); - } - } - - static DisposableContentSource createNew(Observable source) { - final ArrayList chunks = new ArrayList<>(); - ConnectableObservable replay = source.doOnNext(new Action1() { - @Override - public void call(X x) { - chunks.add(x); - } - }).replay(); - return new DisposableContentSource<>(new OnSubscribeImpl(replay, chunks)); - } - - private static class OnSubscribeImpl implements OnSubscribe { - - private final ConnectableObservable source; - private final ArrayList chunks; - private boolean subscribed; - private final AtomicBoolean disposed = new AtomicBoolean(); - - public OnSubscribeImpl(ConnectableObservable source, ArrayList chunks) { - this.source = source; - this.chunks = chunks; - } - - @Override - public void call(Subscriber subscriber) { - - if (disposed.get()) { - subscriber.onError(new IllegalStateException("Content source is already disposed.")); - } - - boolean connectNow = false; - - synchronized (this) { - if (!subscribed) { - connectNow = true; - subscribed = true; - } - } - - source.doOnNext(new Action1() { - @Override - public void call(T msg) { - ReferenceCountUtil.retain(msg); - } - }).unsafeSubscribe(subscriber); - - if (connectNow) { - source.connect(); - } - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/EmitConnectionEvent.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/EmitConnectionEvent.java deleted file mode 100644 index 690be4c..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/EmitConnectionEvent.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandler; - -/** - * An event to indicate to {@link AbstractConnectionToChannelBridge} that the channel is ready to emit a new - * {@link io.reactivex.netty.channel.Connection} to the subscriber as published by {@link ChannelSubscriberEvent} - * - *

Why do we need this?

- * - * Since, emitting a connection can include a handshake for protocols such as TLS/SSL, it is not so that a new - * {@link io.reactivex.netty.channel.Connection} should be emitted as soon as the channel is active (i.e. inside - * {@link ChannelInboundHandler#channelActive(ChannelHandlerContext)}). - * For this reason, this event leaves it to the pipeline or any other entity outside to decide, when is the rite time to - * emit a connection. - */ -public final class EmitConnectionEvent { - - public static final EmitConnectionEvent INSTANCE = new EmitConnectionEvent(); - - private EmitConnectionEvent() { - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/FlushSelectorOperator.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/FlushSelectorOperator.java deleted file mode 100644 index cfaec37..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/FlushSelectorOperator.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import rx.Observable.Operator; -import rx.Subscriber; -import rx.functions.Func1; - -public class FlushSelectorOperator implements Operator { - - private final Func1 flushSelector; - private final ChannelOperations channelOps; - - public FlushSelectorOperator(Func1 flushSelector, ChannelOperations channelOps) { - this.flushSelector = flushSelector; - this.channelOps = channelOps; - } - - @Override - public Subscriber call(final Subscriber subscriber) { - - return new Subscriber(subscriber) { - @Override - public void onCompleted() { - subscriber.onCompleted(); - } - - @Override - public void onError(Throwable e) { - subscriber.onError(e); - } - - @Override - public void onNext(T next) { - subscriber.onNext(next); - /*Call the selector _after_ writing an element*/ - if (flushSelector.call(next)) { - channelOps.flush(); - } - } - }; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/MarkAwarePipeline.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/MarkAwarePipeline.java deleted file mode 100644 index 3f290da..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/MarkAwarePipeline.java +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundInvoker; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.ChannelProgressivePromise; -import io.netty.channel.ChannelPromise; -import io.netty.util.concurrent.EventExecutorGroup; - -import java.net.SocketAddress; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -/** - * An implementation of {@link ChannelPipeline} that allows a mark-reset scheme for {@link ChannelHandler}s. This allows - * temporary modifications to the underlying {@link ChannelPipeline} instance for usecases like pooled connections, - * server response upgrades, etc. - * - * This only supports a single mark at a time, although mark-reset-mark cycles can be repeated any number of times. - * - *

Usage:

- * - * To start recording resetable changes, call {@link #mark()} and to reset back to the state before {@link #mark()} was - * called, call {@link #reset()} - * - *

Thread safety

- * - * All operations of {@link ChannelPipeline} are delegated to the passed {@link ChannelPipeline} instance while - * creation. {@link #mark()} and {@link #reset()} uses the same mutex as {@link ChannelPipeline} for synchronization - * across different method calls. - */ -public final class MarkAwarePipeline implements ChannelPipeline { - - private boolean marked; // Guarded by this - - private final ChannelPipeline delegate; - - public MarkAwarePipeline(ChannelPipeline delegate) { - this.delegate = delegate; - } - - /** - * Marks this pipeline and record further changes which can be reverted by calling {@link #reset()} - * - * @throws IllegalStateException If this method is called more than once without calling {@link #reset()} in - * between. - */ - public synchronized MarkAwarePipeline mark() { - if (marked) { - throw new IllegalStateException("Pipeline does not support nested marks."); - } - return this; - } - - /** - * Marks this pipeline and record further changes which can be reverted by calling {@link #reset()} - * - * @throws IllegalStateException If this method is called more than once without calling {@link #reset()} in - * between. - */ - public synchronized MarkAwarePipeline markIfNotYetMarked() { - if (!marked) { - return mark(); - } - return this; - } - - /** - * If {@link #mark()} was called before, resets the pipeline to the state it was before calling {@link #mark()}. - * Otherwise, ignores the reset. - */ - public synchronized MarkAwarePipeline reset() { - if (!marked) { - return this; /*If there is no mark, there is nothing to reset.*/ - } - - marked = false; - - return this; - } - - public synchronized boolean isMarked() { - return marked; - } - - @Override - public ChannelPipeline addFirst(String name, ChannelHandler handler) { - delegate.addFirst(name, handler); - return this; - } - - @Override - public ChannelPipeline addFirst(EventExecutorGroup group, - String name, ChannelHandler handler) { - delegate.addFirst(group, name, handler); - return this; - } - - @Override - public ChannelPipeline addLast(String name, ChannelHandler handler) { - delegate.addLast(name, handler); - return this; - } - - @Override - public ChannelPipeline addLast(EventExecutorGroup group, - String name, ChannelHandler handler) { - delegate.addLast(group, name, handler); - return this; - } - - @Override - public ChannelPipeline addBefore(String baseName, String name, - ChannelHandler handler) { - return delegate.addBefore(baseName, name, handler); - } - - @Override - public ChannelPipeline addBefore(EventExecutorGroup group, - String baseName, String name, - ChannelHandler handler) { - delegate.addBefore(group, baseName, name, handler); - return this; - } - - @Override - public ChannelPipeline addAfter(String baseName, String name, - ChannelHandler handler) { - delegate.addAfter(baseName, name, handler); - return this; - } - - @Override - public ChannelPipeline addAfter(EventExecutorGroup group, - String baseName, String name, - ChannelHandler handler) { - delegate.addAfter(group, baseName, name, handler); - return this; - } - - @Override - public ChannelPipeline addFirst(ChannelHandler... handlers) { - delegate.addFirst(handlers); - return this; - } - - @Override - public ChannelPipeline addFirst(EventExecutorGroup group, - ChannelHandler... handlers) { - delegate.addFirst(group, handlers); - return this; - } - - @Override - public ChannelPipeline addLast(ChannelHandler... handlers) { - delegate.addLast(handlers); - return this; - } - - @Override - public ChannelPipeline addLast(EventExecutorGroup group, - ChannelHandler... handlers) { - delegate.addLast(group, handlers); - return this; - } - - @Override - public ChannelPipeline remove(ChannelHandler handler) { - delegate.remove(handler); - return this; - } - - @Override - public ChannelHandler remove(String name) { - return delegate.remove(name); - } - - @Override - public T remove(Class handlerType) { - return delegate.remove(handlerType); - } - - @Override - public ChannelHandler removeFirst() { - return delegate.removeFirst(); - } - - @Override - public ChannelHandler removeLast() { - return delegate.removeLast(); - } - - @Override - public ChannelPipeline replace(ChannelHandler oldHandler, - String newName, ChannelHandler newHandler) { - delegate.replace(oldHandler, newName, newHandler); - return this; - } - - @Override - public ChannelHandler replace(String oldName, String newName, - ChannelHandler newHandler) { - return delegate.replace(oldName, newName, newHandler); - } - - @Override - public T replace(Class oldHandlerType, String newName, - ChannelHandler newHandler) { - return delegate.replace(oldHandlerType, newName, newHandler); - } - - @Override - public ChannelHandler first() { - return delegate.first(); - } - - @Override - public ChannelHandlerContext firstContext() { - return delegate.firstContext(); - } - - @Override - public ChannelHandler last() { - return delegate.last(); - } - - @Override - public ChannelHandlerContext lastContext() { - return delegate.lastContext(); - } - - @Override - public ChannelHandler get(String name) { - return delegate.get(name); - } - - @Override - public T get(Class handlerType) { - return delegate.get(handlerType); - } - - @Override - public ChannelHandlerContext context(ChannelHandler handler) { - return delegate.context(handler); - } - - @Override - public ChannelHandlerContext context(String name) { - return delegate.context(name); - } - - @Override - public ChannelHandlerContext context(Class handlerType) { - return delegate.context(handlerType); - } - - @Override - public Channel channel() { - return delegate.channel(); - } - - @Override - public List names() { - return delegate.names(); - } - - @Override - public Map toMap() { - return delegate.toMap(); - } - - @Override - public ChannelPipeline fireChannelRegistered() { - delegate.fireChannelRegistered(); - return this; - } - - @Override - public ChannelPipeline fireChannelUnregistered() { - delegate.fireChannelUnregistered(); - return this; - } - - @Override - public ChannelPipeline fireChannelActive() { - delegate.fireChannelActive(); - return this; - } - - @Override - public ChannelPipeline fireChannelInactive() { - delegate.fireChannelInactive(); - return this; - } - - @Override - public ChannelPipeline fireExceptionCaught(Throwable cause) { - delegate.fireExceptionCaught(cause); - return this; - } - - @Override - public ChannelPipeline fireUserEventTriggered(Object event) { - delegate.fireUserEventTriggered(event); - return this; - } - - @Override - public ChannelPipeline fireChannelRead(Object msg) { - delegate.fireChannelRead(msg); - return this; - } - - @Override - public ChannelPipeline fireChannelReadComplete() { - delegate.fireChannelReadComplete(); - return this; - } - - @Override - public ChannelPipeline fireChannelWritabilityChanged() { - delegate.fireChannelWritabilityChanged(); - return this; - } - - @Override - public ChannelFuture bind(SocketAddress localAddress) { - return delegate.bind(localAddress); - } - - @Override - public ChannelFuture connect(SocketAddress remoteAddress) { - return delegate.connect(remoteAddress); - } - - @Override - public ChannelFuture connect(SocketAddress remoteAddress, - SocketAddress localAddress) { - return delegate.connect(remoteAddress, localAddress); - } - - @Override - public ChannelFuture disconnect() { - return delegate.disconnect(); - } - - @Override - public ChannelFuture close() { - return delegate.close(); - } - - @Override - public ChannelFuture deregister() { - return delegate.deregister(); - } - - @Override - public ChannelFuture bind(SocketAddress localAddress, - ChannelPromise promise) { - return delegate.bind(localAddress, promise); - } - - @Override - public ChannelFuture connect(SocketAddress remoteAddress, - ChannelPromise promise) { - return delegate.connect(remoteAddress, promise); - } - - @Override - public ChannelFuture connect(SocketAddress remoteAddress, - SocketAddress localAddress, - ChannelPromise promise) { - return delegate.connect(remoteAddress, localAddress, promise); - } - - @Override - public ChannelFuture disconnect(ChannelPromise promise) { - return delegate.disconnect(promise); - } - - @Override - public ChannelFuture close(ChannelPromise promise) { - return delegate.close(promise); - } - - @Override - public ChannelFuture deregister(ChannelPromise promise) { - return delegate.deregister(promise); - } - - @Override - public ChannelOutboundInvoker read() { - return delegate.read(); - } - - @Override - public ChannelFuture write(Object msg) { - return delegate.write(msg); - } - - @Override - public ChannelFuture write(Object msg, ChannelPromise promise) { - return delegate.write(msg, promise); - } - - @Override - public ChannelPipeline flush() { - return delegate.flush(); - } - - @Override - public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { - return delegate.writeAndFlush(msg, promise); - } - - @Override - public ChannelFuture writeAndFlush(Object msg) { - return delegate.writeAndFlush(msg); - } - - @Override - public ChannelPromise newPromise() { - return delegate.newPromise(); - } - - @Override - public ChannelProgressivePromise newProgressivePromise() { - return delegate.newProgressivePromise(); - } - - @Override - public ChannelFuture newSucceededFuture() { - return delegate.newSucceededFuture(); - } - - @Override - public ChannelFuture newFailedFuture(Throwable cause) { - return delegate.newFailedFuture(cause); - } - - @Override - public ChannelPromise voidPromise() { - return delegate.voidPromise(); - } - - @Override - public Iterator> iterator() { - return delegate.iterator(); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/SubscriberToChannelFutureBridge.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/SubscriberToChannelFutureBridge.java deleted file mode 100644 index b5588e9..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/SubscriberToChannelFutureBridge.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import rx.Subscriber; -import rx.functions.Action0; -import rx.subscriptions.Subscriptions; - -/** - * A bridge to connect a {@link Subscriber} to a {@link ChannelFuture} so that when the {@code subscriber} is - * unsubscribed, the listener will get removed from the {@code future}. Failure to do so for futures that are long - * living, eg: {@link Channel#closeFuture()} will lead to a memory leak where the attached listener will be in the - * listener queue of the future till the channel closes. - * - * In order to bridge the future and subscriber, {@link #bridge(ChannelFuture, Subscriber)} must be called. - */ -public abstract class SubscriberToChannelFutureBridge implements ChannelFutureListener { - - @Override - public final void operationComplete(ChannelFuture future) throws Exception { - if (future.isSuccess()) { - doOnSuccess(future); - } else { - doOnFailure(future, future.cause()); - } - } - - protected abstract void doOnSuccess(ChannelFuture future); - - protected abstract void doOnFailure(ChannelFuture future, Throwable cause); - - /** - * Bridges the passed subscriber and future, which means the following: - * - *
    -
  • Add this listener to the passed future.
  • -
  • Add a callback to the subscriber, such that on unsubscribe this listener is removed from the future.
  • -
- * - * @param future Future to bridge. - * @param subscriber Subscriber to connect to the future. - */ - public void bridge(final ChannelFuture future, Subscriber subscriber) { - future.addListener(this); - subscriber.add(Subscriptions.create(new Action0() { - @Override - public void call() { - future.removeListener(SubscriberToChannelFutureBridge.this); - } - })); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/WriteTransformations.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/WriteTransformations.java deleted file mode 100644 index fce5748..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/WriteTransformations.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.buffer.ByteBufAllocator; - -import java.util.LinkedList; -import java.util.List; - -/** - * A holder for all transformations that are applied on a channel. Out of the box, it comes with a {@code String} and - * {@code byte[]} transformer to {@code ByteBuf}. Additional transformations can be applied using - * {@link #appendTransformer(AllocatingTransformer)}. - */ -public class WriteTransformations { - - private TransformerChain transformers; - - public boolean transform(Object msg, ByteBufAllocator allocator, List out) { - - boolean transformed = false; - - if (msg instanceof String) { - out.add(allocator.buffer().writeBytes(((String) msg).getBytes())); - transformed = true; - } else if (msg instanceof byte[]) { - out.add(allocator.buffer().writeBytes((byte[]) msg)); - transformed = true; - } else if (null != transformers && transformers.acceptMessage(msg)) { - out.addAll(transformers.transform(msg, allocator)); - transformed = true; - } - - return transformed; - } - - public void appendTransformer(AllocatingTransformer transformer) { - transformers = new TransformerChain(transformer, transformers); - } - - public void resetTransformations() { - transformers = null; - } - - public boolean acceptMessage(Object msg) { - return msg instanceof String || msg instanceof byte[] || null != transformers && transformers.acceptMessage(msg); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private static class TransformerChain extends AllocatingTransformer { - - private final AllocatingTransformer start; - private final AllocatingTransformer next; - - public TransformerChain(AllocatingTransformer start, AllocatingTransformer next) { - this.start = start; - this.next = next; - } - - @Override - public List transform(Object toTransform, ByteBufAllocator allocator) { - if (null == next) { - return start.transform(toTransform, allocator); - } - - List transformed = start.transform(toTransform, allocator); - if (transformed.size() == 1) { - return next.transform(transformed.get(0), allocator); - } else { - final LinkedList toReturn = new LinkedList(); - for (Object nextItem : transformed) { - toReturn.addAll(next.transform(nextItem, allocator)); - } - return toReturn; - } - } - - @Override - protected boolean acceptMessage(Object msg) { - return start.acceptMessage(msg); - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/WriteTransformer.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/WriteTransformer.java deleted file mode 100644 index 786739a..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/WriteTransformer.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageCodec; -import io.netty.util.ReferenceCountUtil; -import io.reactivex.netty.client.ClientConnectionToChannelBridge.ConnectionReuseEvent; - -import java.util.List; - -/** - * A {@link ChannelHandler} that transforms objects written to this channel.

- * - * Any {@code String} or {@code byte[]} written to the channel are converted to {@code ByteBuf} if no other - * {@link AllocatingTransformer} is added that accepts these types. - * - * If the last added {@link AllocatingTransformer} accepts the written message, then invoke all added transformers and - * skip the primitive conversions. - */ -public class WriteTransformer extends MessageToMessageCodec { - - private final WriteTransformations transformations = new WriteTransformations(); - - @Override - public boolean acceptInboundMessage(Object msg) throws Exception { - return false; - } - - @Override - public boolean acceptOutboundMessage(Object msg) throws Exception { - return true;// Always return true and let the encode do the checking as opposed to be done at both places. - } - - @Override - protected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception { - if (!transformations.transform(msg, ctx.alloc(), out)) { - /* - * M2MCodec will release the passed message after encode but we are adding the same object to out. - * So, the message needs to be retained and subsequently released by the next consumer in the pipeline. - */ - out.add(ReferenceCountUtil.retain(msg)); - } - } - - @Override - protected void decode(ChannelHandlerContext ctx, Object msg, List out) throws Exception { - // Never decode (acceptInbound) always returns false. - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof AppendTransformerEvent) { - @SuppressWarnings("rawtypes") - AppendTransformerEvent ate = (AppendTransformerEvent) evt; - transformations.appendTransformer(ate.getTransformer()); - } else if(evt instanceof ConnectionReuseEvent) { - transformations.resetTransformations(); - } - - super.userEventTriggered(ctx, evt); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/events/ConnectionEventListener.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/events/ConnectionEventListener.java deleted file mode 100644 index 7e5e9f4..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/events/ConnectionEventListener.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel.events; - -import io.reactivex.netty.events.EventListener; - -import java.util.concurrent.TimeUnit; - -/** - * An event listener for all events releated to a {@link io.reactivex.netty.channel.Connection} - */ -public abstract class ConnectionEventListener implements EventListener { - - /** - * Event whenever any bytes are read on any open connection. - * - * @param bytesRead Number of bytes read. - */ - @SuppressWarnings("unused") - public void onByteRead(long bytesRead) { } - - /** - * Event whenever any bytes are successfully written on any open connection. - * - * @param bytesWritten Number of bytes written. - */ - @SuppressWarnings("unused") - public void onByteWritten(long bytesWritten) { } - - /** - * Event whenever a flush is issued on a connection. - */ - public void onFlushStart() {} - - /** - * Event whenever flush completes. - * - * @param duration Duration between flush start and completion. - * @param timeUnit Timeunit for the duration. - */ - @SuppressWarnings("unused") - public void onFlushComplete(long duration, TimeUnit timeUnit) {} - - /** - * Event whenever a write is issued on a connection. - */ - public void onWriteStart() {} - - /** - * Event whenever data is written successfully on a connection. Use {@link #onByteWritten(long)} to capture number - * of bytes written. - * - * @param duration Duration between write start and completion. - * @param timeUnit Timeunit for the duration. - */ - @SuppressWarnings("unused") - public void onWriteSuccess(long duration, TimeUnit timeUnit) {} - - /** - * Event whenever a write failed on a connection. - * - * @param duration Duration between write start and failure. - * @param timeUnit Timeunit for the duration. - * @param throwable Error that caused the failure.. - */ - @SuppressWarnings("unused") - public void onWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) {} - - /** - * Event whenever a close of any connection is issued. This event will only be fired when the physical connection - * is closed and not when a pooled connection is closed and put back in the pool. - */ - @SuppressWarnings("unused") - public void onConnectionCloseStart() {} - - /** - * Event whenever a close of any connection is successful. - * - * @param duration Duration between close start and completion. - * @param timeUnit Timeunit for the duration. - */ - @SuppressWarnings("unused") - public void onConnectionCloseSuccess(long duration, TimeUnit timeUnit) {} - - /** - * Event whenever a connection close failed. - * - * @param duration Duration between close start and failure. - * @param timeUnit Timeunit for the duration. - * @param throwable Error that caused the failure. - */ - @SuppressWarnings("unused") - public void onConnectionCloseFailed(long duration, TimeUnit timeUnit, Throwable throwable) {} - - @Override - public void onCustomEvent(Object event) { } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { } - - @Override - public void onCompleted() { } - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/channel/events/ConnectionEventPublisher.java b/netty-http-rx/src/main/java/io/reactivex/netty/channel/events/ConnectionEventPublisher.java deleted file mode 100644 index 87f28cf..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/channel/events/ConnectionEventPublisher.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel.events; - -import io.reactivex.netty.events.EventListener; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.events.EventSource; -import io.reactivex.netty.events.ListenersHolder; -import rx.Subscription; -import rx.functions.Action1; -import rx.functions.Action2; -import rx.functions.Action3; -import rx.functions.Action4; -import rx.functions.Action5; - -import java.util.concurrent.TimeUnit; - -/** - * A publisher which is both {@link EventSource} and {@link EventListener} for connection events. - * - * @param Type of listener to expect. - */ -public final class ConnectionEventPublisher extends ConnectionEventListener - implements EventSource, EventPublisher { - - private final Action2 bytesReadAction = new Action2() { - @Override - public void call(T l, Long bytesRead) { - l.onByteRead(bytesRead); - } - }; - - private final Action2 bytesWrittenAction = new Action2() { - @Override - public void call(T l, Long bytesWritten) { - l.onByteWritten(bytesWritten); - } - }; - - private final Action1 flushStartAction = new Action1() { - @Override - public void call(T l) { - l.onFlushStart(); - } - }; - - private final Action3 flushCompleteAction = new Action3() { - @Override - public void call(T l, Long duration, TimeUnit timeUnit) { - l.onFlushComplete(duration, timeUnit); - } - }; - - private final Action1 writeStartAction = new Action1() { - @Override - public void call(T l) { - l.onWriteStart(); - } - }; - - private final Action3 writeSuccessAction = new Action3() { - @Override - public void call(T l, Long duration, TimeUnit timeUnit) { - l.onWriteSuccess(duration, timeUnit); - } - }; - - private final Action4 writeFailedAction = - new Action4() { - @Override - public void call(T l, Long duration, TimeUnit timeUnit, Throwable t) { - l.onWriteFailed(duration, timeUnit, t); - } - }; - - private final Action1 closeStartAction = new Action1() { - @Override - public void call(T l) { - l.onConnectionCloseStart(); - } - }; - - private final Action3 closeSuccessAction = new Action3() { - @Override - public void call(T l, Long duration, TimeUnit timeUnit) { - l.onConnectionCloseSuccess(duration, timeUnit); - } - }; - - private final Action4 closeFailedAction = - new Action4() { - @Override - public void call(T l, Long duration, TimeUnit timeUnit, Throwable t) { - l.onConnectionCloseFailed(duration, timeUnit, t); - } - }; - - private final Action2 customEventAction = new Action2() { - @Override - public void call(T l, Object event) { - l.onCustomEvent(event); - } - }; - - private final Action3 customEventErrorAction = new Action3() { - @Override - public void call(T l, Throwable throwable, Object event) { - l.onCustomEvent(event, throwable); - } - }; - - private final Action4 customEventDurationAction = new Action4() { - @Override - public void call(T l, Long duration, TimeUnit timeUnit, Object event) { - l.onCustomEvent(event, duration, timeUnit); - } - }; - - private final Action5 customEventDurationErrAction = - new Action5() { - @Override - public void call(T l, Long duration, TimeUnit timeUnit, Throwable throwable, Object event) { - l.onCustomEvent(event, duration, timeUnit, throwable); - } - }; - - private final ListenersHolder listeners; - - public ConnectionEventPublisher() { - listeners = new ListenersHolder<>(); - } - - public ConnectionEventPublisher(ConnectionEventPublisher toCopy) { - listeners = toCopy.listeners.copy(); - } - - @Override - public void onByteRead(final long bytesRead) { - listeners.invokeListeners(bytesReadAction, bytesRead); - } - - @Override - public void onByteWritten(long bytesWritten) { - listeners.invokeListeners(bytesWrittenAction, bytesWritten); - } - - @Override - public void onFlushStart() { - listeners.invokeListeners(flushStartAction); - } - - @Override - public void onFlushComplete(final long duration, final TimeUnit timeUnit) { - listeners.invokeListeners(flushCompleteAction, duration, timeUnit); - } - - @Override - public void onWriteStart() { - listeners.invokeListeners(writeStartAction); - } - - @Override - public void onWriteSuccess(final long duration, final TimeUnit timeUnit) { - listeners.invokeListeners(writeSuccessAction, duration, timeUnit); - } - - @Override - public void onWriteFailed(final long duration, final TimeUnit timeUnit, final Throwable throwable) { - listeners.invokeListeners(writeFailedAction, duration, timeUnit, throwable); - } - - @Override - public void onConnectionCloseStart() { - listeners.invokeListeners(closeStartAction); - } - - @Override - public void onConnectionCloseSuccess(final long duration, final TimeUnit timeUnit) { - listeners.invokeListeners(closeSuccessAction, duration, timeUnit); - } - - @Override - public void onConnectionCloseFailed(final long duration, final TimeUnit timeUnit, final Throwable throwable) { - listeners.invokeListeners(closeFailedAction, duration, timeUnit, throwable); - } - - @Override - public void onCustomEvent(Object event) { - listeners.invokeListeners(customEventAction, event); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { - listeners.invokeListeners(customEventDurationAction, duration, timeUnit, event); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { - listeners.invokeListeners(customEventDurationErrAction, duration, timeUnit, throwable, event); - } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { - listeners.invokeListeners(customEventErrorAction, throwable, event); - } - - @Override - public Subscription subscribe(T listener) { - return listeners.subscribe(listener); - } - - @Override - public boolean publishingEnabled() { - return listeners.publishingEnabled(); - } - - public ConnectionEventPublisher copy() { - return new ConnectionEventPublisher<>(this); - } - - /*Visible for testing*/ ListenersHolder getListeners() { - return listeners; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/ChannelProvider.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/ChannelProvider.java deleted file mode 100644 index ad63f27..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/ChannelProvider.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client; - -import io.netty.channel.Channel; -import rx.Observable; - -public interface ChannelProvider { - - Observable newChannel(Observable input); - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/ChannelProviderFactory.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/ChannelProviderFactory.java deleted file mode 100644 index 7d6c78a..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/ChannelProviderFactory.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client; - -import io.reactivex.netty.client.events.ClientEventListener; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.events.EventSource; - -public interface ChannelProviderFactory { - - ChannelProvider newProvider(Host host, EventSource eventSource, - EventPublisher publisher, ClientEventListener clientPublisher); - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/ClientConnectionToChannelBridge.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/ClientConnectionToChannelBridge.java deleted file mode 100644 index 1f9b443..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/ClientConnectionToChannelBridge.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.util.AttributeKey; -import io.reactivex.netty.channel.AbstractConnectionToChannelBridge; -import io.reactivex.netty.channel.ChannelSubscriberEvent; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.ConnectionInputSubscriberResetEvent; -import io.reactivex.netty.channel.EmitConnectionEvent; -import io.reactivex.netty.client.events.ClientEventListener; -import io.reactivex.netty.client.pool.PooledConnection; -import io.reactivex.netty.events.EventAttributeKeys; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.internal.ExecuteInEventloopAction; -import java.util.logging.Level; -import java.util.logging.Logger; -import rx.Subscriber; -import rx.functions.Action1; -import rx.functions.Actions; -import rx.observers.SafeSubscriber; -import rx.subscriptions.Subscriptions; - -/** - * An implementation of {@link AbstractConnectionToChannelBridge} for clients. - * - *

Reuse

- * - * A channel can be reused for multiple operations, provided the reuses is signalled by {@link ConnectionReuseEvent}. - * Failure to do so, will result in errors on the {@link Subscriber} trying to reuse the channel. - * A typical reuse should have the following events: - * -
-    ChannelSubscriberEvent => ConnectionInputSubscriberEvent => ConnectionReuseEvent =>
-    ConnectionInputSubscriberEvent => ConnectionReuseEvent => ConnectionInputSubscriberEvent
- 
- * - * @param Type read from the connection held by this handler. - * @param Type written to the connection held by this handler. - */ -public class ClientConnectionToChannelBridge extends AbstractConnectionToChannelBridge { - - public static final AttributeKey DISCARD_CONNECTION = AttributeKey.valueOf("rxnetty_discard_connection"); - - private static final Logger logger = Logger.getLogger(ClientConnectionToChannelBridge.class.getName()); - private static final String HANDLER_NAME = "client-conn-channel-bridge"; - - private EventPublisher eventPublisher; - private ClientEventListener eventListener; - private final boolean isSecure; - private Channel channel; - - private ClientConnectionToChannelBridge(boolean isSecure) { - super(HANDLER_NAME, EventAttributeKeys.CONNECTION_EVENT_LISTENER, EventAttributeKeys.EVENT_PUBLISHER); - this.isSecure = isSecure; - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - channel = ctx.channel(); - eventPublisher = channel.attr(EventAttributeKeys.EVENT_PUBLISHER).get(); - eventListener = ctx.channel().attr(EventAttributeKeys.CLIENT_EVENT_LISTENER).get(); - - if (null == eventPublisher) { - logger.log(Level.SEVERE, "No Event publisher bound to the channel, closing channel."); - ctx.channel().close(); - return; - } - - if (eventPublisher.publishingEnabled() && null == eventListener) { - logger.log(Level.SEVERE, "No Event listener bound to the channel and event publishing is enabled., closing channel."); - ctx.channel().close(); - return; - } - - super.handlerAdded(ctx); - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - if (!isSecure) {/*When secure, the event is triggered post SSL handshake via the SslCodec*/ - userEventTriggered(ctx, EmitConnectionEvent.INSTANCE); - } - super.channelActive(ctx); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - - super.userEventTriggered(ctx, evt); // Super handles ConnectionInputSubscriberResetEvent to reset the subscriber. - - if (evt instanceof ConnectionReuseEvent) { - @SuppressWarnings("unchecked") - ConnectionReuseEvent event = (ConnectionReuseEvent) evt; - - newConnectionReuseEvent(ctx.channel(), event); - } - } - - @Override - protected void onNewReadSubscriber(Subscriber subscriber) { - // Unsubscribe from the input closes the connection as there can only be one subscriber to the - // input and, if nothing is read, it means, nobody is using the connection. - // For fire-and-forget usecases, one should explicitly ignore content on the connection which - // adds a discard all subscriber that never unsubscribes. For this case, then, the close becomes - // explicit. - subscriber.add(Subscriptions.create(new ExecuteInEventloopAction(channel) { - @Override - public void run() { - if (!connectionInputSubscriberExists(channel)) { - Connection connection = channel.attr(Connection.CONNECTION_ATTRIBUTE_KEY).get(); - if (null != connection) { - connection.closeNow(); - } - } - } - })); - } - - private void newConnectionReuseEvent(Channel channel, final ConnectionReuseEvent event) { - Subscriber> subscriber = event.getSubscriber(); - if (isValidToEmit(subscriber)) { - subscriber.onNext(event.getPooledConnection()); - checkEagerSubscriptionIfConfigured(channel); - } else { - // If pooled connection not sent to the subscriber, release to the pool. - event.getPooledConnection().close(false).subscribe(Actions.empty(), new Action1() { - @Override - public void call(Throwable throwable) { - logger.log(Level.SEVERE, "Error closing connection.", throwable); - } - }); - } - } - - public static ClientConnectionToChannelBridge addToPipeline(ChannelPipeline pipeline, - boolean isSecure) { - ClientConnectionToChannelBridge toAdd = new ClientConnectionToChannelBridge<>(isSecure); - pipeline.addLast(HANDLER_NAME, toAdd); - return toAdd; - } - - /** - * An event to indicate channel/{@link Connection} reuse. This event should be used for clients that pool - * connections. For every reuse of a connection (connection creation still uses {@link ChannelSubscriberEvent}) - * the corresponding subscriber must be sent via this event. - * - * Every instance of this event resets the older subscriber attached to the connection and connection input. This - * means sending an {@link Subscriber#onCompleted()} to both of those subscribers. It is assumed that the actual - * {@link Subscriber} is similar to {@link SafeSubscriber} which can handle duplicate terminal events. - * - * @param Type read from the connection held by the event. - * @param Type written to the connection held by the event. - */ - public static final class ConnectionReuseEvent implements ConnectionInputSubscriberResetEvent { - - private final Subscriber> subscriber; - private final PooledConnection pooledConnection; - - public ConnectionReuseEvent(Subscriber> subscriber, - PooledConnection pooledConnection) { - this.subscriber = subscriber; - this.pooledConnection = pooledConnection; - } - - public Subscriber> getSubscriber() { - return subscriber; - } - - public PooledConnection getPooledConnection() { - return pooledConnection; - } - } - - /** - * An event to indicate release of a {@link PooledConnection}. - */ - public static final class PooledConnectionReleaseEvent { - - public static final PooledConnectionReleaseEvent INSTANCE = new PooledConnectionReleaseEvent(); - - private PooledConnectionReleaseEvent() { - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/ClientState.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/ClientState.java deleted file mode 100644 index 0a243b6..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/ClientState.java +++ /dev/null @@ -1,505 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.ChannelPromise; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.epoll.EpollSocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.util.concurrent.EventExecutorGroup; -import io.reactivex.netty.RxNetty; -import io.reactivex.netty.channel.ChannelSubscriberEvent; -import io.reactivex.netty.channel.ConnectionCreationFailedEvent; -import io.reactivex.netty.channel.DetachedChannelPipeline; -import io.reactivex.netty.channel.WriteTransformer; -import io.reactivex.netty.client.events.ClientEventListener; -import io.reactivex.netty.events.Clock; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.events.EventSource; -import io.reactivex.netty.ssl.DefaultSslCodec; -import io.reactivex.netty.ssl.SslCodec; -import io.reactivex.netty.util.LoggingHandlerFactory; -import rx.Observable; -import rx.exceptions.Exceptions; -import rx.functions.Action1; -import rx.functions.Func0; -import rx.functions.Func1; - -import javax.net.ssl.SSLEngine; -import java.net.SocketAddress; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; - -import static io.reactivex.netty.HandlerNames.*; -import static java.util.concurrent.TimeUnit.NANOSECONDS; - - -/** - * A collection of state that a client holds. This supports the copy-on-write semantics of clients. - * - * @param The type of objects written to the client owning this state. - * @param The type of objects read from the client owning this state. - */ -public class ClientState { - - private final Observable hostStream; - private final ConnectionProviderFactory factory; - private final DetachedChannelPipeline detachedPipeline; - private final Map, Object> options; - private final boolean isSecure; - private final EventLoopGroup eventLoopGroup; - private final Class channelClass; - private final ChannelProviderFactory channelProviderFactory; - - protected ClientState(Observable hostStream, ConnectionProviderFactory factory, - DetachedChannelPipeline detachedPipeline, EventLoopGroup eventLoopGroup, - Class channelClass) { - this.eventLoopGroup = eventLoopGroup; - this.channelClass = channelClass; - options = new LinkedHashMap<>(); /// Same as netty bootstrap, order matters. - this.hostStream = hostStream; - this.factory = factory; - this.detachedPipeline = detachedPipeline; - isSecure = false; - channelProviderFactory = new ChannelProviderFactory() { - @Override - public ChannelProvider newProvider(Host host, EventSource eventSource, - EventPublisher publisher, ClientEventListener clientPublisher) { - return new ChannelProvider() { - @Override - public Observable newChannel(Observable input) { - return input; - } - }; - } - }; - } - - protected ClientState(ClientState toCopy, ChannelOption option, Object value) { - options = new LinkedHashMap<>(toCopy.options); // Since, we are adding an option, copy it. - options.put(option, value); - detachedPipeline = toCopy.detachedPipeline; - hostStream = toCopy.hostStream; - factory = toCopy.factory; - eventLoopGroup = toCopy.eventLoopGroup; - channelClass = toCopy.channelClass; - isSecure = toCopy.isSecure; - channelProviderFactory = toCopy.channelProviderFactory; - } - - protected ClientState(ClientState toCopy, DetachedChannelPipeline newPipeline, boolean secure) { - final ClientState toCopyCast = toCopy.cast(); - options = toCopy.options; - hostStream = toCopy.hostStream; - factory = toCopyCast.factory; - eventLoopGroup = toCopy.eventLoopGroup; - channelClass = toCopy.channelClass; - detachedPipeline = newPipeline; - isSecure = secure; - channelProviderFactory = toCopyCast.channelProviderFactory; - } - - protected ClientState(ClientState toCopy, ChannelProviderFactory newFactory) { - final ClientState toCopyCast = toCopy.cast(); - options = toCopy.options; - hostStream = toCopy.hostStream; - factory = toCopyCast.factory; - eventLoopGroup = toCopy.eventLoopGroup; - channelClass = toCopy.channelClass; - detachedPipeline = toCopy.detachedPipeline; - channelProviderFactory = newFactory; - isSecure = toCopy.isSecure; - } - - protected ClientState(ClientState toCopy, SslCodec sslCodec) { - this(toCopy, toCopy.detachedPipeline.copy(new TailHandlerFactory(true)).configure(sslCodec), true); - } - - public ClientState channelOption(ChannelOption option, T value) { - return new ClientState<>(this, option, value); - } - - public ClientState addChannelHandlerFirst(String name, Func0 handlerFactory) { - ClientState copy = copy(); - copy.detachedPipeline.addFirst(name, handlerFactory); - return copy; - } - - public ClientState addChannelHandlerFirst(EventExecutorGroup group, String name, - Func0 handlerFactory) { - ClientState copy = copy(); - copy.detachedPipeline.addFirst(group, name, handlerFactory); - return copy; - } - - public ClientState addChannelHandlerLast(String name, Func0 handlerFactory) { - ClientState copy = copy(); - copy.detachedPipeline.addLast(name, handlerFactory); - return copy; - } - - public ClientState addChannelHandlerLast(EventExecutorGroup group, String name, - Func0 handlerFactory) { - ClientState copy = copy(); - copy.detachedPipeline.addLast(group, name, handlerFactory); - return copy; - } - - public ClientState addChannelHandlerBefore(String baseName, String name, - Func0 handlerFactory) { - ClientState copy = copy(); - copy.detachedPipeline.addBefore(baseName, name, handlerFactory); - return copy; - } - - public ClientState addChannelHandlerBefore(EventExecutorGroup group, String baseName, - String name, Func0 handlerFactory) { - ClientState copy = copy(); - copy.detachedPipeline.addBefore(group, baseName, name, handlerFactory); - return copy; - } - - public ClientState addChannelHandlerAfter(String baseName, String name, - Func0 handlerFactory) { - ClientState copy = copy(); - copy.detachedPipeline.addAfter(baseName, name, handlerFactory); - return copy; - } - - public ClientState addChannelHandlerAfter(EventExecutorGroup group, String baseName, - String name, Func0 handlerFactory) { - ClientState copy = copy(); - copy.detachedPipeline.addAfter(group, baseName, name, handlerFactory); - return copy; - } - - public ClientState pipelineConfigurator(Action1 pipelineConfigurator) { - ClientState copy = copy(); - copy.detachedPipeline.configure(pipelineConfigurator); - return copy; - } - - public ClientState enableWireLogging(final LogLevel wireLoggingLevel) { - return enableWireLogging(LoggingHandler.class.getName(), wireLoggingLevel); - } - - public ClientState enableWireLogging(String name, final LogLevel wireLoggingLevel) { - return addChannelHandlerFirst(WireLogging.getName(), - LoggingHandlerFactory.getFactory(name, wireLoggingLevel)); - } - - public static ClientState create(ConnectionProviderFactory factory, - Observable hostStream) { - return create(newChannelPipeline(new TailHandlerFactory(false)), factory, hostStream); - } - - public static ClientState create(ConnectionProviderFactory factory, - Observable hostStream, - EventLoopGroup eventLoopGroup, - Class channelClass) { - return new ClientState<>(hostStream, factory, newChannelPipeline(new TailHandlerFactory(false)), eventLoopGroup, - channelClass); - } - - public static ClientState create(DetachedChannelPipeline detachedPipeline, - ConnectionProviderFactory factory, - Observable hostStream) { - return create(detachedPipeline, factory, hostStream, defaultEventloopGroup(), defaultSocketChannelClass()); - } - - public static ClientState create(DetachedChannelPipeline detachedPipeline, - ConnectionProviderFactory factory, - Observable hostStream, - EventLoopGroup eventLoopGroup, - Class channelClass) { - return new ClientState<>(hostStream, factory, detachedPipeline, eventLoopGroup, channelClass); - } - - private static DetachedChannelPipeline newChannelPipeline(TailHandlerFactory thf) { - return new DetachedChannelPipeline(thf) - .addLast(WriteTransformer.getName(), new Func0() { - @Override - public ChannelHandler call() { - return new WriteTransformer(); - } - }); - } - - public Bootstrap newBootstrap(final EventPublisher eventPublisher, final ClientEventListener eventListener) { - final Bootstrap nettyBootstrap = new Bootstrap().group(eventLoopGroup) - .channel(channelClass) - .option(ChannelOption.AUTO_READ, false);// by default do not read content unless asked. - - for (Entry, Object> optionEntry : options.entrySet()) { - // Type is just for safety for user of ClientState, internally in Bootstrap, types are thrown on the floor. - @SuppressWarnings("unchecked") - ChannelOption key = (ChannelOption) optionEntry.getKey(); - nettyBootstrap.option(key, optionEntry.getValue()); - } - - nettyBootstrap.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.pipeline().addLast(ClientChannelActiveBufferingHandler.getName(), - new ChannelActivityBufferingHandler(eventPublisher, eventListener)); - } - }); - return nettyBootstrap; - } - - public DetachedChannelPipeline unsafeDetachedPipeline() { - return detachedPipeline; - } - - public Map, Object> unsafeChannelOptions() { - return options; - } - - public ClientState channelProviderFactory(ChannelProviderFactory factory) { - return new ClientState<>(this, factory); - } - - public ClientState secure(Func1 sslEngineFactory) { - return secure(new DefaultSslCodec(sslEngineFactory)); - } - - public ClientState secure(SSLEngine sslEngine) { - return secure(new DefaultSslCodec(sslEngine)); - } - - public ClientState secure(SslCodec sslCodec) { - return new ClientState<>(this, sslCodec); - } - - public ClientState unsafeSecure() { - return secure(new DefaultSslCodec(new Func1() { - @Override - public SSLEngine call(ByteBufAllocator allocator) { - try { - return SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .build() - .newEngine(allocator); - } catch (Exception e) { - throw Exceptions.propagate(e); - } - } - })); - } - - private ClientState copy() { - TailHandlerFactory newTail = new TailHandlerFactory(isSecure); - return new ClientState<>(this, detachedPipeline.copy(newTail), isSecure); - } - - public ConnectionProviderFactory getFactory() { - return factory; - } - - public Observable getHostStream() { - return hostStream; - } - - public ChannelProviderFactory getChannelProviderFactory() { - return channelProviderFactory; - } - - @SuppressWarnings("unchecked") - private ClientState cast() { - return (ClientState) this; - } - - protected static class TailHandlerFactory implements Action1 { - - private final boolean isSecure; - - public TailHandlerFactory(boolean isSecure) { - this.isSecure = isSecure; - } - - @Override - public void call(ChannelPipeline pipeline) { - ClientConnectionToChannelBridge.addToPipeline(pipeline, isSecure); - } - } - - public static EventLoopGroup defaultEventloopGroup() { - return RxNetty.getRxEventLoopProvider().globalClientEventLoop(true); - } - - public static Class defaultSocketChannelClass() { - return RxNetty.isUsingNativeTransport() ? EpollSocketChannel.class : NioSocketChannel.class; - } - - /** - * Clients construct the pipeline, outside of the {@link ChannelInitializer} through {@link ChannelProvider}. - * Thus channel registration and activation events may be lost due to a race condition when the channel is active - * before the pipeline is configured. - * This handler buffers, the channel events till the time, a subscriber appears for channel establishment. - */ - private static class ChannelActivityBufferingHandler extends ChannelDuplexHandler { - - private enum State { - Initialized, - Registered, - Active, - Inactive, - ChannelSubscribed - } - - private State state = State.Initialized; - - /** - * Unregistered state will hide the active/inactive state, hence this is a different flag. - */ - private boolean unregistered; - private long connectStartTimeNanos; - private final EventPublisher eventPublisher; - private final ClientEventListener eventListener; - - private ChannelActivityBufferingHandler(EventPublisher eventPublisher, ClientEventListener eventListener) { - this.eventPublisher = eventPublisher; - this.eventListener = eventListener; - } - - @SuppressWarnings("unchecked") - @Override - public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, - ChannelPromise promise) throws Exception { - - connectStartTimeNanos = Clock.newStartTimeNanos(); - - if (eventPublisher.publishingEnabled()) { - eventListener.onConnectStart(); - promise.addListener(new ChannelFutureListener() { - @SuppressWarnings("unchecked") - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (eventPublisher.publishingEnabled()) { - long endTimeNanos = Clock.onEndNanos(connectStartTimeNanos); - if (!future.isSuccess()) { - eventListener.onConnectFailed(endTimeNanos, NANOSECONDS, future.cause()); - } else { - eventListener.onConnectSuccess(endTimeNanos, NANOSECONDS); - } - } - } - }); - } - - super.connect(ctx, remoteAddress, localAddress, promise); - } - - @Override - public void channelRegistered(ChannelHandlerContext ctx) throws Exception { - if (State.ChannelSubscribed == state) { - super.channelRegistered(ctx); - } else { - state = State.Registered; - } - } - - @Override - public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { - if (State.ChannelSubscribed == state) { - super.channelUnregistered(ctx); - } else { - unregistered = true; - } - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - if (State.ChannelSubscribed == state) { - super.channelActive(ctx); - } else { - state = State.Active; - } - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - if (State.ChannelSubscribed == state) { - super.channelInactive(ctx); - } else { - state = State.Inactive; - } - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof ChannelSubscriberEvent) { - final State existingState = state; - state = State.ChannelSubscribed; - super.userEventTriggered(ctx, evt); - final ChannelPipeline pipeline = ctx.channel().pipeline(); - switch (existingState) { - case Initialized: - break; - case Registered: - pipeline.fireChannelRegistered(); - break; - case Active: - pipeline.fireChannelRegistered(); - pipeline.fireChannelActive(); - break; - case Inactive: - pipeline.fireChannelRegistered(); - pipeline.fireChannelActive(); - pipeline.fireChannelInactive(); - break; - case ChannelSubscribed: - // Duplicate event, ignore. - break; - } - - if (unregistered) { - pipeline.fireChannelUnregistered(); - } - } else if (evt instanceof ConnectionCreationFailedEvent) { - ConnectionCreationFailedEvent failedEvent = (ConnectionCreationFailedEvent) evt; - onConnectFailedEvent(failedEvent); - super.userEventTriggered(ctx, evt); - } else { - super.userEventTriggered(ctx, evt); - } - } - - @SuppressWarnings("unchecked") - private void onConnectFailedEvent(ConnectionCreationFailedEvent event) { - if (eventPublisher.publishingEnabled()) { - eventListener.onConnectFailed(connectStartTimeNanos, NANOSECONDS, event.getThrowable()); - } - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/ConnectionProvider.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/ConnectionProvider.java deleted file mode 100644 index 0c0d6e4..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/ConnectionProvider.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client; - -import io.reactivex.netty.channel.Connection; -import rx.Observable; - -/** - * A contract to control how connections are established from a client. - * - * @param The type of objects written on the connections created by this provider. - * @param The type of objects read from the connections created by this provider. - */ -public interface ConnectionProvider { - - /** - * Returns an {@code Observable} that emits a single connection every time it is subscribed. - * - * @return An {@code Observable} that emits a single connection every time it is subscribed. - */ - Observable> newConnectionRequest(); - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/ConnectionProviderFactory.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/ConnectionProviderFactory.java deleted file mode 100644 index 335085a..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/ConnectionProviderFactory.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client; - -import rx.Observable; - -public interface ConnectionProviderFactory { - - ConnectionProvider newProvider(Observable> hosts); - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/ConnectionRequest.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/ConnectionRequest.java deleted file mode 100644 index 595da5f..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/ConnectionRequest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client; - -import io.reactivex.netty.channel.Connection; -import rx.Observable; - -/** - * A connection request that is used to create connections for different protocols. - * - *

Mutations

- * - * All mutations to this request that creates a brand new instance. - * - *

Inititating connections

- * - * A new connection is initiated every time {@link ConnectionRequest#subscribe()} is called and is the only way of - * creating connections. - * - * @param The type of the objects that are written to the connection created by this request. - * @param The type of objects that are read from the connection created by this request. - */ -public abstract class ConnectionRequest extends Observable> { - - protected ConnectionRequest(OnSubscribe> f) { - super(f); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/Host.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/Host.java deleted file mode 100644 index afae0e3..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/Host.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client; - -import rx.Observable; - -import java.net.SocketAddress; - -public final class Host { - - private final SocketAddress host; - private final Observable closeNotifier; - - public Host(SocketAddress host) { - this(host, Observable.never()); - } - - public Host(SocketAddress host, Observable closeNotifier) { - this.host = host; - this.closeNotifier = closeNotifier; - } - - public SocketAddress getHost() { - return host; - } - - public Observable getCloseNotifier() { - return closeNotifier; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Host)) { - return false; - } - - Host host1 = (Host) o; - - if (host != null? !host.equals(host1.host) : host1.host != null) { - return false; - } - return closeNotifier != null? closeNotifier.equals(host1.closeNotifier) : host1.closeNotifier == null; - - } - - @Override - public int hashCode() { - int result = host != null? host.hashCode() : 0; - result = 31 * result + (closeNotifier != null? closeNotifier.hashCode() : 0); - return result; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/HostConnector.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/HostConnector.java deleted file mode 100644 index e95d5e5..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/HostConnector.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client; - -import io.reactivex.netty.client.events.ClientEventListener; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.events.EventSource; -import rx.Subscription; - -public class HostConnector implements EventSource { - - private final Host host; - private final ConnectionProvider connectionProvider; - @SuppressWarnings("rawtypes") - private final EventSource eventSource; - private final EventPublisher publisher; - private final ClientEventListener clientPublisher; - - public HostConnector(Host host, ConnectionProvider connectionProvider, - EventSource eventSource, EventPublisher publisher, - ClientEventListener clientPublisher) { - this.host = host; - this.connectionProvider = connectionProvider; - this.eventSource = eventSource; - this.publisher = publisher; - this.clientPublisher = clientPublisher; - } - - public HostConnector(HostConnector source, ConnectionProvider connectionProvider) { - this.connectionProvider = connectionProvider; - host = source.host; - eventSource = source.eventSource; - clientPublisher = source.clientPublisher; - publisher = source.publisher; - } - - public Host getHost() { - return host; - } - - public ConnectionProvider getConnectionProvider() { - return connectionProvider; - } - - public ClientEventListener getClientPublisher() { - return clientPublisher; - } - - public EventPublisher getEventPublisher() { - return publisher; - } - - @Override - @SuppressWarnings("unchecked") - public Subscription subscribe(ClientEventListener listener) { - return eventSource.subscribe(listener); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof HostConnector)) { - return false; - } - - HostConnector that = (HostConnector) o; - - if (host != null? !host.equals(that.host) : that.host != null) { - return false; - } - if (connectionProvider != null? !connectionProvider.equals(that.connectionProvider) : - that.connectionProvider != null) { - return false; - } - if (eventSource != null? !eventSource.equals(that.eventSource) : that.eventSource != null) { - return false; - } - if (publisher != null? !publisher.equals(that.publisher) : that.publisher != null) { - return false; - } - return clientPublisher != null? clientPublisher.equals(that.clientPublisher) : that.clientPublisher == null; - - } - - @Override - public int hashCode() { - int result = host != null? host.hashCode() : 0; - result = 31 * result + (connectionProvider != null? connectionProvider.hashCode() : 0); - result = 31 * result + (eventSource != null? eventSource.hashCode() : 0); - result = 31 * result + (publisher != null? publisher.hashCode() : 0); - result = 31 * result + (clientPublisher != null? clientPublisher.hashCode() : 0); - return result; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/events/ClientEventListener.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/events/ClientEventListener.java deleted file mode 100644 index 0325c60..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/events/ClientEventListener.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client.events; - -import io.reactivex.netty.channel.events.ConnectionEventListener; - -import java.util.concurrent.TimeUnit; - -public class ClientEventListener extends ConnectionEventListener { - /** - * Event whenever a new connection attempt is made. - */ - @SuppressWarnings("unused") - public void onConnectStart() {} - - /** - * Event whenever a new connection is successfully established. - * - * @param duration Duration between connect start and completion. - * @param timeUnit Timeunit for the duration. - */ - @SuppressWarnings("unused") - public void onConnectSuccess(long duration, TimeUnit timeUnit) {} - - /** - * Event whenever a connect attempt failed. - * - * @param duration Duration between connect start and failure. - * @param timeUnit Timeunit for the duration. - * @param throwable Error that caused the failure. - */ - @SuppressWarnings("unused") - public void onConnectFailed(long duration, TimeUnit timeUnit, Throwable throwable) {} - - /** - * Event whenever a connection release to the pool is initiated (by closing the connection) - */ - @SuppressWarnings("unused") - public void onPoolReleaseStart() {} - - /** - * Event whenever a connection is successfully released to the pool. - * - * @param duration Duration between release start and completion. - * @param timeUnit Timeunit for the duration. - */ - @SuppressWarnings("unused") - public void onPoolReleaseSuccess(long duration, TimeUnit timeUnit) {} - - /** - * Event whenever a connection release to pool fails. - * - * @param duration Duration between release start and failure. - * @param timeUnit Timeunit for the duration. - * @param throwable Error that caused the failure. - */ - @SuppressWarnings("unused") - public void onPoolReleaseFailed(long duration, TimeUnit timeUnit, Throwable throwable) {} - - /** - * Event whenever an idle connection is removed/evicted from the pool. - */ - @SuppressWarnings("unused") - public void onPooledConnectionEviction() {} - - /** - * Event whenever a connection is reused from the pool. - */ - @SuppressWarnings("unused") - public void onPooledConnectionReuse() {} - - /** - * Event whenever an acquire from the pool is initiated. - */ - @SuppressWarnings("unused") - public void onPoolAcquireStart() {} - - /** - * Event whenever an acquire from the pool is successful. - * - * @param duration Duration between acquire start and completion. - * @param timeUnit Timeunit for the duration. - */ - @SuppressWarnings("unused") - public void onPoolAcquireSuccess(long duration, TimeUnit timeUnit) {} - - /** - * Event whenever an acquire from the pool failed. - * - * @param duration Duration between acquire start and failure. - * @param timeUnit Timeunit for the duration. - * @param throwable Error that caused the failure. - */ - @SuppressWarnings("unused") - public void onPoolAcquireFailed(long duration, TimeUnit timeUnit, Throwable throwable) {} -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/internal/SingleHostConnectionProvider.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/internal/SingleHostConnectionProvider.java deleted file mode 100644 index 01d0086..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/internal/SingleHostConnectionProvider.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client.internal; - -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.client.ConnectionProvider; -import io.reactivex.netty.client.HostConnector; -import java.util.logging.Level; -import java.util.logging.Logger; -import rx.Observable; -import rx.functions.Action1; - -/** - * A connection provider that only ever fetches a single host from the host stream provided to it. - * - * @param The type of objects written on the connections created by this provider. - * @param The type of objects read from the connections created by this provider. - */ -public class SingleHostConnectionProvider implements ConnectionProvider { - - private static final Logger logger = Logger.getLogger(SingleHostConnectionProvider.class.getName()); - - private volatile ConnectionProvider provider; - - public SingleHostConnectionProvider(Observable> connectors) { - connectors.toSingle() - .subscribe(connector -> provider = connector.getConnectionProvider(), - t -> logger.log(Level.SEVERE, "Failed while fetching a host connector from a scalar host source", t)); - } - - @Override - public Observable> newConnectionRequest() { - return null != provider ? provider.newConnectionRequest() - : Observable.>error(new IllegalStateException("No hosts available.")); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/AbstractP2CStrategy.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/AbstractP2CStrategy.java deleted file mode 100644 index 11dd776..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/AbstractP2CStrategy.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client.loadbalancer; - -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.client.ConnectionProvider; -import io.reactivex.netty.client.Host; -import io.reactivex.netty.client.HostConnector; -import io.reactivex.netty.client.events.ClientEventListener; -import rx.Observable; - -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; - -public abstract class AbstractP2CStrategy implements LoadBalancingStrategy { - - @Override - public ConnectionProvider newStrategy(final List> hosts) { - newHostsList(hosts.size()); - return new ConnectionProvider() { - - @Override - public Observable> newConnectionRequest() { - HostHolder selected = null; - if (hosts.isEmpty()) { - noUsableHostsFound(); - return Observable.error(NoHostsAvailableException.EMPTY_INSTANCE); - } else if (hosts.size() == 1) { - HostHolder holder = hosts.get(0); - @SuppressWarnings("unchecked") - L eventListener = (L) holder.getEventListener(); - double weight = getWeight(eventListener); - if (isUnusable(weight)) { - noUsableHostsFound(); - return Observable.error(new NoHostsAvailableException("No usable hosts found.")); - } - selected = holder; - } else { - ThreadLocalRandom rand = ThreadLocalRandom.current(); - for (int i = 0; i < 5; i++) { - int pos = rand.nextInt(hosts.size()); - HostHolder first = hosts.get(pos); - int pos2 = (rand.nextInt(hosts.size() - 1) + pos + 1) % hosts.size(); - HostHolder second = hosts.get(pos2); - - @SuppressWarnings("unchecked") - double w1 = getWeight((L) first.getEventListener()); - @SuppressWarnings("unchecked") - double w2 = getWeight((L) second.getEventListener()); - - if (w1 > w2) { - selected = first; - break; - } else if (w1 < w2) { - selected = second; - break; - } else if (!isUnusable(w1)) { - selected = first; - break; - } - foundTwoUnusableHosts(); - } - if (null == selected) { - noUsableHostsFound(); - return Observable.error(new NoHostsAvailableException("No usable hosts found after 5 tries.")); - } - } - - return selected.getConnector().getConnectionProvider().newConnectionRequest(); - } - }; - } - - protected boolean isUnusable(double weight) { - return weight < 0.0; - } - - @Override - public HostHolder toHolder(HostConnector connector) { - return new HostHolder<>(connector, newListener(connector.getHost())); - } - - protected abstract L newListener(Host host); - - protected abstract double getWeight(L listener); - - protected void noUsableHostsFound() { - // No Op by default - } - - protected void foundTwoUnusableHosts() { - // No Op by default - } - - protected void newHostsList(int size) { - // No Op by default - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/HostCollector.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/HostCollector.java deleted file mode 100644 index 9170413..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/HostCollector.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client.loadbalancer; - -import rx.Single; -import rx.functions.Func1; - -import java.util.List; - -public interface HostCollector { - - Func1, Single>>> newCollector(); - - final class HostUpdate { - - public enum Action{ Add, Remove } - - private final Action action; - private final HostHolder hostHolder; - - public HostUpdate(Action action, HostHolder hostHolder) { - this.action = action; - this.hostHolder = hostHolder; - } - - public Action getAction() { - return action; - } - - public HostHolder getHostHolder() { - return hostHolder; - } - } - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/HostHolder.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/HostHolder.java deleted file mode 100644 index 050c060..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/HostHolder.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client.loadbalancer; - -import io.reactivex.netty.client.HostConnector; -import io.reactivex.netty.client.events.ClientEventListener; - -public class HostHolder { - - private final HostConnector connector; - private final ClientEventListener eventListener; - - public HostHolder(HostConnector connector, ClientEventListener eventListener) { - this.connector = connector; - this.eventListener = eventListener; - } - - public HostConnector getConnector() { - return connector; - } - - public ClientEventListener getEventListener() { - return eventListener; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof HostHolder)) { - return false; - } - - HostHolder that = (HostHolder) o; - - if (connector != null? !connector.equals(that.connector) : that.connector != null) { - return false; - } - return eventListener != null? eventListener.equals(that.eventListener) : that.eventListener == null; - - } - - @Override - public int hashCode() { - int result = connector != null? connector.hashCode() : 0; - result = 31 * result + (eventListener != null? eventListener.hashCode() : 0); - return result; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/LoadBalancerFactory.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/LoadBalancerFactory.java deleted file mode 100644 index cf4aa03..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/LoadBalancerFactory.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client.loadbalancer; - -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.client.ConnectionProvider; -import io.reactivex.netty.client.ConnectionProviderFactory; -import io.reactivex.netty.client.HostConnector; -import io.reactivex.netty.client.loadbalancer.HostCollector.HostUpdate; -import io.reactivex.netty.client.loadbalancer.HostCollector.HostUpdate.Action; -import io.reactivex.netty.internal.VoidToAnythingCast; -import java.util.logging.Level; -import java.util.logging.Logger; -import rx.Observable; -import rx.Single; -import rx.functions.Func1; - -import java.util.List; - -public class LoadBalancerFactory implements ConnectionProviderFactory { - - private static final Logger logger = Logger.getLogger(LoadBalancerFactory.class.getName()); - - private final LoadBalancingStrategy strategy; - private final HostCollector collector; - - private LoadBalancerFactory(LoadBalancingStrategy strategy, HostCollector collector) { - this.strategy = strategy; - this.collector = collector; - } - - @Override - public ConnectionProvider newProvider(Observable> hosts) { - - return new ConnectionProviderImpl(hosts.map(connector -> { - HostHolder newHolder = strategy.toHolder(connector); - connector.subscribe(newHolder.getEventListener()); - return newHolder; - }).flatMap((Func1, Observable>>) holder -> holder.getConnector() - .getHost() - .getCloseNotifier() - .map(new VoidToAnythingCast>()) - .ignoreElements() - .onErrorResumeNext(Observable.>empty()) - .concatWith(Observable.just(new HostUpdate<>(Action.Remove, holder))) - .mergeWith(Observable.just(new HostUpdate<>(Action.Add, holder)))).flatMap(newCollector(collector.newCollector()), 1).distinctUntilChanged()); - } - - public static LoadBalancerFactory create(LoadBalancingStrategy strategy) { - return create(strategy, new NoBufferHostCollector()); - } - - public static LoadBalancerFactory create(LoadBalancingStrategy strategy, - HostCollector collector) { - return new LoadBalancerFactory<>(strategy, collector); - } - - private class ConnectionProviderImpl implements ConnectionProvider { - - private volatile ConnectionProvider currentProvider = () -> - Observable.error(NoHostsAvailableException.EMPTY_INSTANCE); - - public ConnectionProviderImpl(Observable>> hosts) { - hosts.subscribe(hostHolders -> currentProvider = strategy.newStrategy(hostHolders), - throwable -> logger.log(Level.SEVERE, "Error while listening on the host stream. Hosts will not be refreshed.", throwable)); - } - - @Override - public Observable> newConnectionRequest() { - return currentProvider.newConnectionRequest(); - } - } - - private Func1, ? extends Observable>>> - newCollector(final Func1, Single>>> f) { - return (Func1, Observable>>>) holder -> - f.call(holder).toObservable(); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/LoadBalancingStrategy.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/LoadBalancingStrategy.java deleted file mode 100644 index 6aa4d7e..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/LoadBalancingStrategy.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client.loadbalancer; - -import io.reactivex.netty.client.ConnectionProvider; -import io.reactivex.netty.client.HostConnector; - -import java.util.List; - -public interface LoadBalancingStrategy { - - ConnectionProvider newStrategy(List> hosts); - - HostHolder toHolder(HostConnector connector); - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/NoBufferHostCollector.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/NoBufferHostCollector.java deleted file mode 100644 index 40c3e61..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/NoBufferHostCollector.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client.loadbalancer; - -import rx.Single; -import rx.functions.Func1; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * A {@link HostCollector} implementation that does not buffer any updates and hence emits a new list for every new - * host received or a host removed. - */ -public class NoBufferHostCollector implements HostCollector { - - private final boolean allowDuplicates; - - public NoBufferHostCollector() { - this(false); - } - - public NoBufferHostCollector(boolean allowDuplicates) { - this.allowDuplicates = allowDuplicates; - } - - @Override - public Func1, Single>>> newCollector() { - return new Func1, Single>>>() { - - private volatile List> currentList = Collections.emptyList(); - - @Override - public Single>> call(HostUpdate update) { - List> newList = null; - - switch (update.getAction()) { - case Add: - if (allowDuplicates || !currentList.contains(update.getHostHolder())) { - newList = new ArrayList<>(currentList); - newList.add(update.getHostHolder()); - } - break; - case Remove: - newList = new ArrayList<>(currentList); - newList.remove(update.getHostHolder()); - break; - } - - if (null != newList) { - currentList = newList; - } - - return Single.just(currentList); - } - }; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/NoHostsAvailableException.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/NoHostsAvailableException.java deleted file mode 100644 index 34aa530..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/loadbalancer/NoHostsAvailableException.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client.loadbalancer; - -import io.netty.util.internal.EmptyArrays; - -/** - * Exception raised when there are no eligible hosts available to a load balancer. - */ -public class NoHostsAvailableException extends RuntimeException { - - private static final long serialVersionUID = 7993688893506534768L; - - @SuppressWarnings("ThrowableInstanceNeverThrown") - public static final NoHostsAvailableException EMPTY_INSTANCE = new NoHostsAvailableException(); - - static { - EMPTY_INSTANCE.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE); - } - - public NoHostsAvailableException() { - } - - public NoHostsAvailableException(String message) { - super(message); - } - - public NoHostsAvailableException(String message, Throwable cause) { - super(message, cause); - } - - public NoHostsAvailableException(Throwable cause) { - super(cause); - } - - public NoHostsAvailableException(String message, Throwable cause, boolean enableSuppression, - boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/CompositePoolLimitDeterminationStrategy.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/CompositePoolLimitDeterminationStrategy.java deleted file mode 100644 index 239f748..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/CompositePoolLimitDeterminationStrategy.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -import java.util.concurrent.TimeUnit; - -public class CompositePoolLimitDeterminationStrategy implements PoolLimitDeterminationStrategy { - - private final PoolLimitDeterminationStrategy[] strategies; - - public CompositePoolLimitDeterminationStrategy(PoolLimitDeterminationStrategy... strategies) { - if (null == strategies || strategies.length == 0) { - throw new IllegalArgumentException("Strategies can not be null or empty."); - } - for (PoolLimitDeterminationStrategy strategy : strategies) { - if (null == strategy) { - throw new IllegalArgumentException("No strategy can be null."); - } - } - this.strategies = strategies; - } - - @Override - public boolean acquireCreationPermit(long acquireStartTime, TimeUnit timeUnit) { - for (int i = 0; i < strategies.length; i++) { - PoolLimitDeterminationStrategy strategy = strategies[i]; - if (!strategy.acquireCreationPermit(acquireStartTime, timeUnit)) { - if (i > 0) { - for (int j = i - 1; j >= 0; j--) { - strategies[j].releasePermit(); // release all permits acquired before this failure. - } - } - return false; - } - } - return true; // nothing failed and hence it is OK to create a new connection. - } - - /** - * Returns the minimum number of permits available across all strategies. - * - * @return The minimum number of permits available across all strategies. - */ - @Override - public int getAvailablePermits() { - int minPermits = Integer.MAX_VALUE; - for (PoolLimitDeterminationStrategy strategy : strategies) { - int availablePermits = strategy.getAvailablePermits(); - minPermits = Math.min(minPermits, availablePermits); - } - return minPermits; // If will atleast be one strategy (invariant in constructor) and hence this should be the value provided by that strategy. - } - - @Override - public void releasePermit() { - for (PoolLimitDeterminationStrategy strategy : strategies) { - strategy.releasePermit(); - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/FIFOIdleConnectionsHolder.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/FIFOIdleConnectionsHolder.java deleted file mode 100644 index 434a580..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/FIFOIdleConnectionsHolder.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -import rx.Observable; -import rx.Observable.OnSubscribe; -import rx.Subscriber; - -import java.util.concurrent.ConcurrentLinkedQueue; - -/** - * An implementation of {@link IdleConnectionsHolder} with a FIFO strategy. - * - * @param Type of object that is written to the client using this holder. - * @param Type of object that is read from the the client using this holder. - */ -public class FIFOIdleConnectionsHolder extends IdleConnectionsHolder { - - private final ConcurrentLinkedQueue> idleConnections; - private final Observable> pollObservable; - private final Observable> peekObservable; - - public FIFOIdleConnectionsHolder() { - idleConnections = new ConcurrentLinkedQueue<>(); - - pollObservable = Observable.create(new OnSubscribe>() { - @Override - public void call(Subscriber> subscriber) { - PooledConnection idleConnection; - while (!subscriber.isUnsubscribed() && (idleConnection = idleConnections.poll()) != null) { - subscriber.onNext(idleConnection); - } - if (!subscriber.isUnsubscribed()) { - subscriber.onCompleted(); - } - } - }); - - peekObservable = Observable.from(idleConnections); - } - - @Override - public Observable> poll() { - return pollObservable; - } - - @Override - public Observable> peek() { - return peekObservable; - } - - @Override - public void add(PooledConnection toAdd) { - idleConnections.add(toAdd); - } - - @Override - public boolean remove(PooledConnection toRemove) { - return idleConnections.remove(toRemove); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/IdleConnectionsHolder.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/IdleConnectionsHolder.java deleted file mode 100644 index 35dda3e..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/IdleConnectionsHolder.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -import io.netty.channel.EventLoop; -import rx.Observable; - -/** - * A holder of idle {@link PooledConnection} used by {@link PooledConnectionProvider} - * - * @param Type of object that is written to the client using this holder. - * @param Type of object that is read from the the client using this holder. - */ -public abstract class IdleConnectionsHolder { - - /** - * Creates a stream of idle connections where every item sent on to the stream is removed from the underlying - * idle connections pool. - * - * @return A stream of idle connections. - */ - public abstract Observable> poll(); - - /** - * Creates a stream of idle connections where every item sent on to the stream is removed from the underlying - * idle connections pool. - * This method will only poll connections if the calling thread is an {@link EventLoop} known to this holder. - * Otherwise, it should return an empty stream. - * - * @return A stream of idle connections. - */ - public Observable> pollThisEventLoopConnections() { - return poll(); /*Override if the holder is aware of eventloops*/ - } - - /** - * Creates a stream of idle connections where every item sent on to the stream is NOT removed from the - * underlying idle connections pool. If the connection is to be removed, {@link #remove(PooledConnection)} must - * be called for that connection. - * - * @return A stream of idle connections. - */ - public abstract Observable> peek(); - - /** - * Adds the passed connection to this holder. - * - * @param toAdd Connection to add. - */ - public abstract void add(PooledConnection toAdd); - - /** - * Removes the passed connection from this holder. - * - * @param toRemove Connection to remove. - */ - public abstract boolean remove(PooledConnection toRemove); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/MaxConnectionsBasedStrategy.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/MaxConnectionsBasedStrategy.java deleted file mode 100644 index 18ab9a0..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/MaxConnectionsBasedStrategy.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * An implementation of {@link PoolLimitDeterminationStrategy} that limits the pool based on a maximum connections limit. - * This limit can be increased or decreased at runtime. - */ -public class MaxConnectionsBasedStrategy implements PoolLimitDeterminationStrategy { - - public static final int DEFAULT_MAX_CONNECTIONS = 1000; - - private final AtomicInteger limitEnforcer; - private final AtomicInteger maxConnections; - - public MaxConnectionsBasedStrategy() { - this(DEFAULT_MAX_CONNECTIONS); - } - - public MaxConnectionsBasedStrategy(int maxConnections) { - this.maxConnections = new AtomicInteger(maxConnections); - limitEnforcer = new AtomicInteger(); - } - - @Override - public boolean acquireCreationPermit(long acquireStartTime, TimeUnit timeUnit) { - /** - * As opposed to limitEnforcer.incrementAndGet() we follow this model as this does not change the limitEnforcer - * value unless there are enough permits. - * If we were to use incrementAndGet(), in case of overflow (from max allowed limit) we would have to decrement - * the limitEnforcer. This may show temporary overflows in getMaxConnections() which may be disturbing for a - * user. However, even if we use incrementAndGet() the counter corrects itself over time. - * This is just a more semantically correct implementation with similar performance characterstics as - * incrementAndGet() - */ - for (;;) { - final int currentValue = limitEnforcer.get(); - final int newValue = currentValue + 1; - final int maxAllowedConnections = maxConnections.get(); - if (newValue <= maxAllowedConnections) { - if (limitEnforcer.compareAndSet(currentValue, newValue)) { - return true; - } - } else { - return false; - } - } - } - - public int incrementMaxConnections(int incrementBy) { - return maxConnections.addAndGet(incrementBy); - } - - public int decrementMaxConnections(int decrementBy) { - return maxConnections.addAndGet(-1 * decrementBy); - } - - public int getMaxConnections() { - return maxConnections.get(); - } - - @Override - public int getAvailablePermits() { - return maxConnections.get() - limitEnforcer.get(); - } - - @Override - public void releasePermit() { - /** - * As opposed to limitEnforcer.decrementAndGet() we follow this model as this does not change the limitEnforcer - * value unless there are enough permits. - * If we were to use decrementAndGet(), in case of overflow (from max allowed limit) we would have to decrement - * the limitEnforcer. This may show temporary overflows in getMaxConnections() which may be disturbing for a - * user. However, even if we use decrementAndGet() the counter corrects itself over time. - * This is just a more semantically correct implementation with similar performance characterstics as - * decrementAndGet() - */ - for (;;) { - final int currentValue = limitEnforcer.get(); - final int newValue = currentValue - 1; - if (newValue >= 0) { - if (!limitEnforcer.compareAndSet(currentValue, newValue)) { - continue; - } - } - break; - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PoolConfig.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PoolConfig.java deleted file mode 100644 index 926748c..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PoolConfig.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -import rx.Observable; - -import java.util.concurrent.TimeUnit; - -/** - * A configuration for connection pooling for a client. - * - * @param Type of object that is written to the client using this pool config. - * @param Type of object that is read from the the client using this pool config. - */ -public class PoolConfig { - - public static final long DEFAULT_MAX_IDLE_TIME_MILLIS = TimeUnit.MILLISECONDS.convert(30, TimeUnit.SECONDS); - - private Observable idleConnCleanupTicker; - private PoolLimitDeterminationStrategy limitDeterminationStrategy; - private IdleConnectionsHolder idleConnectionsHolder; - private long maxIdleTimeMillis; - - public PoolConfig() { - maxIdleTimeMillis = DEFAULT_MAX_IDLE_TIME_MILLIS; - idleConnCleanupTicker = Observable.interval(maxIdleTimeMillis, maxIdleTimeMillis, TimeUnit.MILLISECONDS); - idleConnectionsHolder = new FIFOIdleConnectionsHolder<>(); - limitDeterminationStrategy = UnboundedPoolLimitDeterminationStrategy.INSTANCE; - } - - public long getMaxIdleTimeMillis() { - return maxIdleTimeMillis; - } - - public Observable getIdleConnectionsCleanupTimer() { - return idleConnCleanupTicker; - } - - public PoolLimitDeterminationStrategy getPoolLimitDeterminationStrategy() { - return limitDeterminationStrategy; - } - - public PoolConfig maxConnections(int maxConnections) { - limitDeterminationStrategy = new MaxConnectionsBasedStrategy(maxConnections); - return this; - } - - public PoolConfig maxIdleTimeoutMillis(long maxIdleTimeoutMillis) { - maxIdleTimeMillis = maxIdleTimeoutMillis; - return this; - } - - public PoolConfig limitDeterminationStrategy(PoolLimitDeterminationStrategy strategy) { - limitDeterminationStrategy = strategy; - return this; - } - - public PoolLimitDeterminationStrategy getLimitDeterminationStrategy() { - return limitDeterminationStrategy; - } - - public PoolConfig idleConnectionsHolder(IdleConnectionsHolder holder) { - idleConnectionsHolder = holder; - return this; - } - - public IdleConnectionsHolder getIdleConnectionsHolder() { - return idleConnectionsHolder; - } - - public PoolConfig idleConnectionsCleanupTimer(Observable timer) { - idleConnCleanupTicker = timer; - return this; - } - - public Observable getIdleConnCleanupTicker() { - return idleConnCleanupTicker; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PoolExhaustedException.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PoolExhaustedException.java deleted file mode 100644 index b0abdb7..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PoolExhaustedException.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -public class PoolExhaustedException extends Exception { - - private static final long serialVersionUID = -6299997509113653123L; - - public PoolExhaustedException() { - } - - public PoolExhaustedException(Throwable cause) { - super(cause); - } - - public PoolExhaustedException(String message) { - super(message); - } - - public PoolExhaustedException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PoolLimitDeterminationStrategy.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PoolLimitDeterminationStrategy.java deleted file mode 100644 index dfeaa64..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PoolLimitDeterminationStrategy.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -import java.util.concurrent.TimeUnit; - -/** - * A strategy to delegate the decision pertaining to connection pool size limits. - */ -public interface PoolLimitDeterminationStrategy { - - /** - * Attempts to acquire a creation permit. - * - * @param acquireStartTime The start time for the acquire process in milliseconds since epoch. - * @param timeUnit The timeunit for the acquire start time. - * - * @return {@code true} if the permit was acquired, {@code false} otherwise. - */ - boolean acquireCreationPermit(long acquireStartTime, TimeUnit timeUnit); - - /** - * Returns the number of creation permits available. - * - * @return The number of creation permits available. - */ - int getAvailablePermits(); - - /** - * Release a previously acquired permit. - */ - void releasePermit(); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PooledConnection.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PooledConnection.java deleted file mode 100644 index 020ce68..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PooledConnection.java +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.FileRegion; -import io.netty.util.AttributeKey; -import io.netty.util.concurrent.EventExecutorGroup; -import io.reactivex.netty.channel.AllocatingTransformer; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.client.ClientConnectionToChannelBridge; -import io.reactivex.netty.client.ClientConnectionToChannelBridge.ConnectionReuseEvent; -import java.util.logging.Level; -import java.util.logging.Logger; -import rx.Observable; -import rx.Observable.OnSubscribe; -import rx.Observable.Transformer; -import rx.Subscriber; -import rx.functions.Action0; -import rx.functions.Action1; -import rx.functions.Actions; -import rx.functions.Func1; - -/** - * An implementation of {@link Connection} which is pooled and reused. - * - * It is required to call {@link #reuse(Subscriber)} for reusing this connection. - * - * @param Type of object that is read from this connection. - * @param Type of object that is written to this connection. - */ -public class PooledConnection extends Connection { - - private static final Logger logger = Logger.getLogger(PooledConnection.class.getName()); - - public static final AttributeKey DYNAMIC_CONN_KEEP_ALIVE_TIMEOUT_MS = - AttributeKey.valueOf("rxnetty_conn_keep_alive_timeout_millis"); - - private final Owner owner; - private final Connection unpooledDelegate; - - private volatile long lastReturnToPoolTimeMillis; - private volatile boolean releasedAtLeastOnce; - private volatile long maxIdleTimeMillis; - private final Observable releaseObservable; - - private PooledConnection(Owner owner, long maxIdleTimeMillis, Connection unpooledDelegate) { - super(unpooledDelegate); - if (null == owner) { - throw new IllegalArgumentException("Pooled connection owner can not be null"); - } - if (null == unpooledDelegate) { - throw new IllegalArgumentException("Connection delegate can not be null"); - } - - this.owner = owner; - this.unpooledDelegate = unpooledDelegate; - this.maxIdleTimeMillis = maxIdleTimeMillis; - lastReturnToPoolTimeMillis = System.currentTimeMillis(); - releaseObservable = Observable.create(new OnSubscribe() { - @Override - public void call(Subscriber subscriber) { - if (!isUsable()) { - PooledConnection.this.owner.discard(PooledConnection.this) - .unsafeSubscribe(subscriber); - } else { - Long keepAliveTimeout = unsafeNettyChannel().attr(DYNAMIC_CONN_KEEP_ALIVE_TIMEOUT_MS).get(); - if (null != keepAliveTimeout) { - PooledConnection.this.maxIdleTimeMillis = keepAliveTimeout; - } - markAwarePipeline.reset(); // Reset pipeline state, if changed, on release. - PooledConnection.this.owner.release(PooledConnection.this) - .doOnCompleted(new Action0() { - @Override - public void call() { - releasedAtLeastOnce = true; - lastReturnToPoolTimeMillis = System.currentTimeMillis(); - } - }) - .unsafeSubscribe(subscriber); - } - } - }).onErrorResumeNext(discard()); - } - - private PooledConnection(PooledConnection toCopy, Connection unpooledDelegate) { - super(unpooledDelegate); - owner = toCopy.owner; - this.unpooledDelegate = unpooledDelegate; - lastReturnToPoolTimeMillis = toCopy.lastReturnToPoolTimeMillis; - releasedAtLeastOnce = toCopy.releasedAtLeastOnce; - maxIdleTimeMillis = toCopy.maxIdleTimeMillis; - releaseObservable = toCopy.releaseObservable; - } - - @Override - public Observable write(Observable msgs) { - return unpooledDelegate.write(msgs); - } - - @Override - public Observable write(Observable msgs, Func1 flushSelector) { - return unpooledDelegate.write(msgs, flushSelector); - } - - @Override - public Observable writeAndFlushOnEach(Observable msgs) { - return unpooledDelegate.writeAndFlushOnEach(msgs); - } - - @Override - public Observable writeString(Observable msgs) { - return unpooledDelegate.writeString(msgs); - } - - @Override - public Observable writeString(Observable msgs, - Func1 flushSelector) { - return unpooledDelegate.writeString(msgs, flushSelector); - } - - @Override - public Observable writeStringAndFlushOnEach(Observable msgs) { - return unpooledDelegate.writeStringAndFlushOnEach(msgs); - } - - @Override - public Observable writeBytes(Observable msgs) { - return unpooledDelegate.writeBytes(msgs); - } - - @Override - public Observable writeBytes(Observable msgs, - Func1 flushSelector) { - return unpooledDelegate.writeBytes(msgs, flushSelector); - } - - @Override - public Observable writeBytesAndFlushOnEach(Observable msgs) { - return unpooledDelegate.writeBytesAndFlushOnEach(msgs); - } - - @Override - public Observable writeFileRegion(Observable msgs) { - return unpooledDelegate.writeFileRegion(msgs); - } - - @Override - public Observable writeFileRegion(Observable msgs, - Func1 flushSelector) { - return unpooledDelegate.writeFileRegion(msgs, flushSelector); - } - - @Override - public Observable writeFileRegionAndFlushOnEach(Observable msgs) { - return unpooledDelegate.writeFileRegionAndFlushOnEach(msgs); - } - - @Override - public void flush() { - unpooledDelegate.flush(); - } - - @Override - public Observable close() { - return close(true); - } - - @Override - public Observable close(boolean flush) { - if (flush) { - return releaseObservable.doOnSubscribe(new Action0() { - @Override - public void call() { - unpooledDelegate.flush(); - } - }); - } else { - return releaseObservable; - } - } - - @Override - public void closeNow() { - close().subscribe(Actions.empty(), new Action1() { - @Override - public void call(Throwable throwable) { - logger.log(Level.SEVERE, "Error closing connection.", throwable); - } - }); - } - - @Override - public Observable closeListener() { - return unpooledDelegate.closeListener(); - } - - @Override - public Connection addChannelHandlerAfter(String baseName, String name, - ChannelHandler handler) { - return new PooledConnection<>(this, unpooledDelegate.addChannelHandlerAfter(baseName, name, handler)); - } - - @Override - public Connection addChannelHandlerAfter(EventExecutorGroup group, - String baseName, String name, - ChannelHandler handler) { - return new PooledConnection<>(this, unpooledDelegate.addChannelHandlerAfter(group, baseName, name, - handler)); - } - - @Override - public Connection addChannelHandlerBefore(String baseName, String name, - ChannelHandler handler) { - return new PooledConnection<>(this, unpooledDelegate.addChannelHandlerBefore(baseName, name, handler)); - } - - @Override - public Connection addChannelHandlerBefore(EventExecutorGroup group, - String baseName, String name, - ChannelHandler handler) { - return new PooledConnection<>(this, unpooledDelegate.addChannelHandlerBefore(group, baseName, name, - handler)); - } - - @Override - public Connection addChannelHandlerFirst(EventExecutorGroup group, - String name, ChannelHandler handler) { - return new PooledConnection<>(this, unpooledDelegate.addChannelHandlerFirst(group, name, handler)); - } - - @Override - public Connection addChannelHandlerFirst(String name, ChannelHandler handler) { - return new PooledConnection<>(this, unpooledDelegate.addChannelHandlerFirst(name, handler)); - } - - @Override - public Connection addChannelHandlerLast(EventExecutorGroup group, - String name, ChannelHandler handler) { - return new PooledConnection<>(this, unpooledDelegate.addChannelHandlerLast(group, name, handler)); - } - - @Override - public Connection addChannelHandlerLast(String name, ChannelHandler handler) { - return new PooledConnection<>(this, unpooledDelegate.addChannelHandlerLast(name, handler)); - } - - @Override - public Connection pipelineConfigurator(Action1 pipelineConfigurator) { - return new PooledConnection<>(this, unpooledDelegate.pipelineConfigurator(pipelineConfigurator)); - } - - @Override - public Connection transformRead(Transformer transformer) { - return new PooledConnection<>(this, unpooledDelegate.transformRead(transformer)); - } - - @Override - public Connection transformWrite(AllocatingTransformer transformer) { - return new PooledConnection<>(this, unpooledDelegate.transformWrite(transformer)); - } - - /** - * Discards this connection, to be called when this connection will never be used again. - * - * @return {@link Observable} representing the result of the discard, this will typically be resulting in a close - * on the underlying {@link Connection}. - */ - /*package private, externally shouldn't be discardable.*/Observable discard() { - return unpooledDelegate.close(); - } - - /** - * Returns whether this connection is safe to be used at this moment. - * This makes sure that the underlying netty's channel is active as returned by - * {@link Channel#isActive()} and it has not passed the maximum idle time in the pool. - * - * @return {@code true} if the connection is usable. - */ - public boolean isUsable() { - final Channel nettyChannel = unsafeNettyChannel(); - Boolean discardConn = nettyChannel.attr(ClientConnectionToChannelBridge.DISCARD_CONNECTION).get(); - - if (!nettyChannel.isActive() || Boolean.TRUE == discardConn) { - return false; - } - - long nowMillis = System.currentTimeMillis(); - long idleTime = nowMillis - lastReturnToPoolTimeMillis; - return idleTime < maxIdleTimeMillis; - } - - /** - * This method must be called for reusing the connection i.e. for sending this connection to the passed subscriber. - * - * @param connectionSubscriber Subscriber for the pooled connection for reuse. - */ - public void reuse(Subscriber> connectionSubscriber) { - unsafeNettyChannel().pipeline().fireUserEventTriggered(new ConnectionReuseEvent<>(connectionSubscriber, this)); - } - - public static PooledConnection create(Owner owner, long maxIdleTimeMillis, - Connection unpooledDelegate) { - final PooledConnection toReturn = new PooledConnection<>(owner, maxIdleTimeMillis, unpooledDelegate - ); - toReturn.connectCloseToChannelClose(); - return toReturn; - } - - /** - * Returns {@code true} if this connection is reused at least once. - * - * @return {@code true} if this connection is reused at least once. - */ - public boolean isReused() { - return releasedAtLeastOnce; - } - - @Override - public ChannelPipeline getChannelPipeline() { - return markAwarePipeline; // Always return mark aware as, we always have to reset state on release to pool. - } - - /*Visible for testin*/ void setLastReturnToPoolTimeMillis(long lastReturnToPoolTimeMillis) { - this.lastReturnToPoolTimeMillis = lastReturnToPoolTimeMillis; - } - - /** - * A contract for the owner of the {@link PooledConnection} to which any instance of {@link PooledConnection} must - * be returned after use. - */ - public interface Owner { - - /** - * Releases the passed connection back to the owner, for reuse. - * - * @param connection Connection to be released. - * - * @return {@link Observable} representing result of the release. Every subscription to this, releases the - * connection. - */ - Observable release(PooledConnection connection); - - /** - * Discards the passed connection from the pool. This is usually called due to an external event like closing of - * a connection that the pool may not know. - * This operation is idempotent and hence can be called multiple times with no side effects - * - * @param connection The connection to discard. - * - * @return {@link Observable} indicating the result of the discard (which usually results in a close()). - * Every subscription to this {@link Observable} will discard the connection. - */ - Observable discard(PooledConnection connection); - - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PooledConnectionProvider.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PooledConnectionProvider.java deleted file mode 100644 index bc3bd28..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PooledConnectionProvider.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -import io.reactivex.netty.client.ConnectionProvider; -import io.reactivex.netty.client.ConnectionProviderFactory; -import io.reactivex.netty.client.HostConnector; -import io.reactivex.netty.client.pool.PooledConnection.Owner; - -/** - * An implementation of {@link PooledConnectionProvider} that pools connections. - * - * Following are the key parameters: - * - *
    -
  • {@link PoolLimitDeterminationStrategy}: A strategy to determine whether a new physical connection should be - created as part of the user request.
  • -
  • {@link PoolConfig#getIdleConnectionsCleanupTimer()}: The schedule for cleaning up idle connections in the pool.
  • -
  • {@link PoolConfig#getMaxIdleTimeMillis()}: Maximum time a connection can be idle in this pool.
  • -
- * - *

Usage

- * - *

Complementing a {@link ConnectionProviderFactory}

- * - * For employing better host selection strategies, this provider can be used to complement the default - * {@link ConnectionProvider} provided by a {@link HostConnector}. - * - *

Standalone

- * - * For clients that do not use a pool of hosts can use {@link SingleHostPoolingProviderFactory} that will only ever pick - * a single host but will pool connections. - */ -public abstract class PooledConnectionProvider implements ConnectionProvider , Owner { - - public static PooledConnectionProvider createUnbounded(final HostConnector delegate) { - return create(new PoolConfig(), delegate); - } - - public static PooledConnectionProvider createBounded(int maxConnections, - final HostConnector delegate) { - return create(new PoolConfig().maxConnections(maxConnections), delegate); - } - - public static PooledConnectionProvider create(final PoolConfig config, - final HostConnector delegate) { - return new PooledConnectionProviderImpl<>(config, delegate); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PooledConnectionProviderImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PooledConnectionProviderImpl.java deleted file mode 100644 index 3b02e25..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PooledConnectionProviderImpl.java +++ /dev/null @@ -1,431 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.client.ClientConnectionToChannelBridge.PooledConnectionReleaseEvent; -import io.reactivex.netty.client.HostConnector; -import io.reactivex.netty.client.events.ClientEventListener; -import io.reactivex.netty.events.Clock; -import io.reactivex.netty.events.EventPublisher; -import java.util.logging.Level; -import java.util.logging.Logger; -import rx.Observable; -import rx.Observable.OnSubscribe; -import rx.Observable.Operator; -import rx.Subscriber; -import rx.Subscription; -import rx.functions.Action0; -import rx.functions.Action1; -import rx.functions.Actions; -import rx.functions.Func1; - -import static io.reactivex.netty.events.EventAttributeKeys.*; -import static java.util.concurrent.TimeUnit.*; - -/** - * An implementation of {@link PooledConnectionProvider} that pools connections. - * - * Following are the key parameters: - * - *
    -
  • {@link PoolLimitDeterminationStrategy}: A strategy to determine whether a new physical connection should be - created as part of the user request.
  • -
  • {@link PoolConfig#getIdleConnectionsCleanupTimer()}: The schedule for cleaning up idle connections in the pool.
  • -
  • {@link PoolConfig#getMaxIdleTimeMillis()}: Maximum time a connection can be idle in this pool.
  • -
- * - * @param Type of object that is written to the client using this factory. - * @param Type of object that is read from the the client using this factory. - */ -public final class PooledConnectionProviderImpl extends PooledConnectionProvider { - - private static final Logger logger = Logger.getLogger(PooledConnectionProviderImpl.class.getName()); - - private final Subscription idleConnCleanupSubscription; - private final IdleConnectionsHolder idleConnectionsHolder; - - private final PoolLimitDeterminationStrategy limitDeterminationStrategy; - private final long maxIdleTimeMillis; - private final HostConnector hostConnector; - private volatile boolean isShutdown; - - public PooledConnectionProviderImpl(PoolConfig poolConfig, HostConnector hostConnector) { - this.hostConnector = hostConnector; - idleConnectionsHolder = poolConfig.getIdleConnectionsHolder(); - limitDeterminationStrategy = poolConfig.getPoolLimitDeterminationStrategy(); - maxIdleTimeMillis = poolConfig.getMaxIdleTimeMillis(); - // In case, there is no cleanup required, this observable should never give a tick. - idleConnCleanupSubscription = poolConfig.getIdleConnCleanupTicker() - .doOnError(LogErrorAction.INSTANCE) - .retry() // Retry when there is an error in timer. - .concatMap(new IdleConnectionCleanupTask()) - .doOnError(new Action1() { - @Override - public void call(Throwable throwable) { - logger.log(Level.SEVERE, "Ignoring error cleaning up idle connections.", - throwable); - } - }) - .retry() - .subscribe(); - - hostConnector.getHost() - .getCloseNotifier() - .doOnTerminate(new Action0() { - @Override - public void call() { - isShutdown = true; - idleConnCleanupSubscription.unsubscribe(); - } - }) - .onErrorResumeNext(new Func1>() { - @Override - public Observable call(Throwable throwable) { - logger.log(Level.SEVERE, "Error listening to Host close notifications. Shutting down the pool.", - throwable); - return Observable.empty(); - } - }) - .subscribe(Actions.empty()); - } - - @Override - public Observable> newConnectionRequest() { - return Observable.create(new OnSubscribe>() { - @Override - public void call(Subscriber> subscriber) { - if (isShutdown) { - subscriber.onError(new IllegalStateException("Connection provider is shutdown.")); - } - idleConnectionsHolder.pollThisEventLoopConnections() - .concatWith(connectIfAllowed()) - .filter(new Func1, Boolean>() { - @Override - public Boolean call(PooledConnection c) { - boolean isUsable = c.isUsable(); - if (!isUsable) { - discardNow(c); - } - return isUsable; - } - }) - .take(1) - .lift(new ReuseSubscriberLinker()) - .lift(new ConnectMetricsOperator()) - .unsafeSubscribe(subscriber); - } - }); - } - - @Override - public Observable release(final PooledConnection connection) { - @SuppressWarnings("unchecked") - final PooledConnection c = (PooledConnection) connection; - return Observable.create(new OnSubscribe() { - @Override - public void call(Subscriber subscriber) { - if (null == c) { - subscriber.onCompleted(); - } else { - /** - * Executing the release on the eventloop to avoid race-conditions between code cleaning up - * connection in the pipeline and the connecting being released to the pool. - */ - c.unsafeNettyChannel() - .eventLoop() - .submit(new ReleaseTask(c, subscriber)); - } - } - }); - } - - @Override - public Observable discard(final PooledConnection connection) { - return connection.discard().doOnSubscribe(new Action0() { - @Override - public void call() { - EventPublisher eventPublisher = connection.unsafeNettyChannel().attr(EVENT_PUBLISHER).get(); - if (eventPublisher.publishingEnabled()) { - ClientEventListener eventListener = connection.unsafeNettyChannel() - .attr(CLIENT_EVENT_LISTENER).get(); - eventListener.onPooledConnectionEviction(); - } - limitDeterminationStrategy.releasePermit();/*Since, an idle connection took a permit*/ - } - }); - } - - private Observable> connectIfAllowed() { - return Observable.create(new OnSubscribe>() { - @Override - public void call(Subscriber> subscriber) { - final long startTimeNanos = Clock.newStartTimeNanos(); - if (limitDeterminationStrategy.acquireCreationPermit(startTimeNanos, NANOSECONDS)) { - Observable> newConnObsv = hostConnector.getConnectionProvider() - .newConnectionRequest(); - newConnObsv.map(new Func1, PooledConnection>() { - @Override - public PooledConnection call(Connection connection) { - return PooledConnection.create(PooledConnectionProviderImpl.this, - maxIdleTimeMillis, connection); - } - }).doOnError(new Action1() { - @Override - public void call(Throwable throwable) { - limitDeterminationStrategy.releasePermit(); /*Before connect we acquired.*/ - } - }).unsafeSubscribe(subscriber); - } else { - idleConnectionsHolder.poll() - .switchIfEmpty(Observable.>error( - new PoolExhaustedException("Client connection pool exhausted."))) - .unsafeSubscribe(subscriber); - } - } - }); - } - - private void discardNow(PooledConnection toDiscard) { - discard(toDiscard).subscribe(Actions.empty(), new Action1() { - @Override - public void call(Throwable throwable) { - logger.log(Level.SEVERE, "Error discarding connection.", throwable); - } - }); - } - - private static class LogErrorAction implements Action1 { - - public static final LogErrorAction INSTANCE = new LogErrorAction(); - - @Override - public void call(Throwable throwable) { - logger.log(Level.SEVERE, "Error from idle connection cleanup timer. This will be retried.", throwable); - } - } - - private class IdleConnectionCleanupTask implements Func1> { - @Override - public Observable call(Long aLong) { - return idleConnectionsHolder.peek() - .map((Func1, Void>) connection -> { - if (!connection.isUsable()) { - idleConnectionsHolder.remove(connection); - discardNow(connection); - } - return null; - }).ignoreElements(); - } - } - - private class ReleaseTask implements Runnable { - - private final PooledConnection connection; - private final Subscriber subscriber; - private final long releaseStartTimeNanos; - private final EventPublisher eventPublisher; - private final ClientEventListener eventListener; - - private ReleaseTask(PooledConnection connection, Subscriber subscriber) { - this.connection = connection; - this.subscriber = subscriber; - releaseStartTimeNanos = Clock.newStartTimeNanos(); - eventPublisher = connection.unsafeNettyChannel().attr(EVENT_PUBLISHER).get(); - eventListener = connection.unsafeNettyChannel().attr(CLIENT_EVENT_LISTENER).get(); - } - - @Override - public void run() { - try { - connection.unsafeNettyChannel().pipeline().fireUserEventTriggered(PooledConnectionReleaseEvent.INSTANCE); - if (eventPublisher.publishingEnabled()) { - eventListener.onPoolReleaseStart(); - } - if (isShutdown || !connection.isUsable()) { - discardNow(connection); - } else { - idleConnectionsHolder.add(connection); - } - - if (eventPublisher.publishingEnabled()) { - eventListener.onPoolReleaseSuccess(Clock.onEndNanos(releaseStartTimeNanos), NANOSECONDS); - } - subscriber.onCompleted(); - } catch (Throwable throwable) { - if (eventPublisher.publishingEnabled()) { - eventListener.onPoolReleaseFailed(Clock.onEndNanos(releaseStartTimeNanos), NANOSECONDS, throwable); - } - subscriber.onError(throwable); - } - } - } - - private class ConnectMetricsOperator implements Operator, PooledConnection> { - - @Override - public Subscriber> call(final Subscriber> o) { - final long startTimeNanos = Clock.newStartTimeNanos(); - - return new Subscriber<>(o) { - - private volatile boolean publishingEnabled; - private volatile ClientEventListener eventListener; - - @Override - public void onCompleted() { - if (publishingEnabled) { - eventListener.onPoolAcquireStart(); - eventListener.onPoolAcquireSuccess(Clock.onEndNanos(startTimeNanos), NANOSECONDS); - } - o.onCompleted(); - } - - @Override - public void onError(Throwable e) { - if (publishingEnabled) { - /*Error means no connection was received, as it always every gets at most one connection*/ - eventListener.onPoolAcquireStart(); - eventListener.onPoolAcquireFailed(Clock.onEndNanos(startTimeNanos), NANOSECONDS, e); - } - o.onError(e); - } - - @Override - public void onNext(PooledConnection c) { - EventPublisher eventPublisher = c.unsafeNettyChannel().attr(EVENT_PUBLISHER).get(); - if (eventPublisher.publishingEnabled()) { - publishingEnabled = true; - eventListener = c.unsafeNettyChannel().attr(CLIENT_EVENT_LISTENER).get(); - } - o.onNext(c); - } - }; - } - } - - private class ReuseSubscriberLinker implements Operator, PooledConnection> { - - private ScalarAsyncSubscriber onReuseSubscriber; - - @Override - public Subscriber> call(final Subscriber> o) { - return new Subscriber<>(o) { - - @Override - public void onCompleted() { - /*This subscriber is not invoked by different threads, so don't need sychronization*/ - if (null != onReuseSubscriber) { - onReuseSubscriber.onCompleted(); - } else { - o.onCompleted(); - } - } - - @Override - public void onError(Throwable e) { - /*This subscriber is not invoked by different threads, so don't need sychronization*/ - if (null != onReuseSubscriber) { - onReuseSubscriber.onError(e); - } else { - o.onError(e); - } - } - - @Override - public void onNext(PooledConnection c) { - if (c.isReused()) { - EventPublisher eventPublisher = c.unsafeNettyChannel().attr(EVENT_PUBLISHER).get(); - if (eventPublisher.publishingEnabled()) { - ClientEventListener eventListener = c.unsafeNettyChannel() - .attr(CLIENT_EVENT_LISTENER).get(); - eventListener.onPooledConnectionReuse(); - } - onReuseSubscriber = new ScalarAsyncSubscriber<>(o); - c.reuse(onReuseSubscriber); /*Reuse will on next to the subscriber*/ - } else { - o.onNext(c); - } - } - }; - } - - } - - private static class ScalarAsyncSubscriber extends Subscriber> { - - private boolean terminated; /*Guarded by this*/ - private Throwable error; /*Guarded by this*/ - private boolean onNextArrived; /*Guarded by this*/ - private final Subscriber> delegate; - - private ScalarAsyncSubscriber(Subscriber> delegate) { - this.delegate = delegate; - } - - @Override - public void onCompleted() { - boolean _onNextArrived; - - synchronized (this) { - _onNextArrived = onNextArrived; - } - - terminated = true; - - if (_onNextArrived) { - delegate.onCompleted(); - } - } - - @Override - public void onError(Throwable e) { - boolean _onNextArrived; - - synchronized (this) { - _onNextArrived = onNextArrived; - } - terminated = true; - error = e; - - if (_onNextArrived) { - delegate.onError(e); - } - } - - @Override - public void onNext(PooledConnection conn) { - boolean _terminated; - Throwable _error; - synchronized (this) { - onNextArrived = true; - _terminated = terminated; - _error = error; - delegate.onNext(conn); - } - - - if (_terminated) { - if (null != error) { - delegate.onError(_error); - } else { - delegate.onCompleted(); - } - } - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PreferCurrentEventLoopHolder.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PreferCurrentEventLoopHolder.java deleted file mode 100644 index b175334..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/PreferCurrentEventLoopHolder.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -import io.netty.channel.EventLoop; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.FastThreadLocal; -import io.reactivex.netty.client.ClientConnectionToChannelBridge; -import io.reactivex.netty.threads.PreferCurrentEventLoopGroup; -import java.util.logging.Level; -import java.util.logging.Logger; -import rx.Observable; -import rx.Observable.OnSubscribe; -import rx.Subscriber; -import rx.functions.Action1; -import rx.functions.Actions; -import rx.functions.Func0; - -import java.util.ArrayList; - -/** - * An {@link IdleConnectionsHolder} implementation that can identify if the calling thread is an {@link EventLoop} in - * the provided {@link PreferCurrentEventLoopGroup} and prefers a connection registered with the calling - * {@link EventLoop}. - * - * If the calling thread is not an {@link EventLoop} in the provided {@link PreferCurrentEventLoopGroup} then - * {@link #poll()} and {@link #peek()} will iterate over connections from all {@link EventLoop}s however - * {@link #add(PooledConnection)} will attempt to find the {@link EventLoop} of the added {@link PooledConnection}. If - * the {@link EventLoop} of the connection does not belong to the provided {@link PreferCurrentEventLoopGroup} then the - * connection will be discarded. - * - * @param Type of object that is written to the client using this holder. - * @param Type of object that is read from the the client using this holder. - */ -public class PreferCurrentEventLoopHolder extends IdleConnectionsHolder { - - private static final Logger logger = Logger.getLogger(PreferCurrentEventLoopHolder.class.getName()); - - private final FastThreadLocal> perElHolder = new FastThreadLocal<>(); - private final ArrayList> allElHolders; - private final Observable> pollObservable; - private final Observable> peekObservable; - - PreferCurrentEventLoopHolder(PreferCurrentEventLoopGroup eventLoopGroup) { - this(eventLoopGroup, new FIFOIdleConnectionsHolderFactory()); - } - - PreferCurrentEventLoopHolder(PreferCurrentEventLoopGroup eventLoopGroup, - final IdleConnectionsHolderFactory holderFactory) { - final ArrayList> _allElHolders = new ArrayList<>(); - allElHolders = _allElHolders; - for (final EventExecutor child : eventLoopGroup) { - final IdleConnectionsHolder newHolder = holderFactory.call(); - allElHolders.add(newHolder); - child.submit(new Runnable() { - @Override - public void run() { - perElHolder.set(newHolder); - } - }); - } - - Observable> pollOverAllHolders = Observable.empty(); - Observable> peekOverAllHolders = Observable.empty(); - - for (IdleConnectionsHolder anElHolder : allElHolders) { - pollOverAllHolders = pollOverAllHolders.concatWith(anElHolder.poll()); - peekOverAllHolders = peekOverAllHolders.concatWith(anElHolder.peek()); - } - - pollObservable = pollOverAllHolders; - peekObservable = peekOverAllHolders; - } - - @Override - public Observable> poll() { - return pollObservable; - } - - @Override - public Observable> pollThisEventLoopConnections() { - - return Observable.create(new OnSubscribe>() { - @Override - public void call(Subscriber> subscriber) { - final IdleConnectionsHolder holderForThisEL = perElHolder.get(); - if (null == holderForThisEL) { - /*Caller is not an eventloop*/ - PreferCurrentEventLoopHolder.super.pollThisEventLoopConnections().unsafeSubscribe(subscriber); - } else { - holderForThisEL.poll().unsafeSubscribe(subscriber); - } - } - }); - } - - @Override - public Observable> peek() { - return peekObservable; - } - - @Override - public void add(final PooledConnection toAdd) { - final IdleConnectionsHolder holderForThisEL = perElHolder.get(); - if (null != holderForThisEL) { - holderForThisEL.add(toAdd); - } else { - /* - * This should not happen as the code generally adds the connection from within an eventloop. - * By executing the add on the eventloop, the owner eventloop is correctly discovered for this eventloop. - */ - toAdd.unsafeNettyChannel().eventLoop().execute(new Runnable() { - @Override - public void run() { - IdleConnectionsHolder holderForThisEl = perElHolder.get(); - if (null == holderForThisEl) { - logger.log(Level.SEVERE, "Unrecognized eventloop: " + Thread.currentThread().getName() + - ". Returned connection can not be added to the pool. Closing the connection."); - toAdd.unsafeNettyChannel().attr(ClientConnectionToChannelBridge.DISCARD_CONNECTION).set(true); - toAdd.close().subscribe(Actions.empty(), new Action1() { - @Override - public void call(Throwable throwable) { - logger.log(Level.SEVERE, "Failed to discard connection.", throwable); - } - }); - } else { - holderForThisEl.add(toAdd); - } - } - }); - } - } - - @Override - public boolean remove(PooledConnection toRemove) { - for (IdleConnectionsHolder anElHolder : allElHolders) { - if (anElHolder.remove(toRemove)) { - return true; - } - } - return false; - } - - public interface IdleConnectionsHolderFactory extends Func0> { - } - - private static class FIFOIdleConnectionsHolderFactory implements IdleConnectionsHolderFactory { - - @Override - public IdleConnectionsHolder call() { - return new FIFOIdleConnectionsHolder<>(); - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/SingleHostPoolingProviderFactory.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/SingleHostPoolingProviderFactory.java deleted file mode 100644 index 3d8d27b..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/SingleHostPoolingProviderFactory.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -import io.reactivex.netty.client.ConnectionProvider; -import io.reactivex.netty.client.ConnectionProviderFactory; -import io.reactivex.netty.client.HostConnector; -import io.reactivex.netty.client.internal.SingleHostConnectionProvider; -import rx.Observable; -import rx.functions.Func1; - -/** - * A {@link ConnectionProviderFactory} that must only be used for a client that operates on a single host. - */ -public class SingleHostPoolingProviderFactory implements ConnectionProviderFactory { - - private final PoolConfig config; - - private SingleHostPoolingProviderFactory(PoolConfig config) { - this.config = config; - } - - @Override - public ConnectionProvider newProvider(Observable> hosts) { - return new SingleHostConnectionProvider<>(hosts.map(new Func1, HostConnector>() { - @Override - public HostConnector call(HostConnector hc) { - return new HostConnector<>(hc, PooledConnectionProvider.create(config, hc)); - } - })); - } - - public static SingleHostPoolingProviderFactory createUnbounded() { - return create(new PoolConfig()); - } - - public static SingleHostPoolingProviderFactory createBounded(int maxConnections) { - return create(new PoolConfig().maxConnections(maxConnections)); - } - - public static SingleHostPoolingProviderFactory create(final PoolConfig config) { - return new SingleHostPoolingProviderFactory<>(config); - } - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/UnboundedPoolLimitDeterminationStrategy.java b/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/UnboundedPoolLimitDeterminationStrategy.java deleted file mode 100644 index 0b9bfec..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/client/pool/UnboundedPoolLimitDeterminationStrategy.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -import java.util.concurrent.TimeUnit; - -public class UnboundedPoolLimitDeterminationStrategy implements PoolLimitDeterminationStrategy { - - public static final PoolLimitDeterminationStrategy INSTANCE = new UnboundedPoolLimitDeterminationStrategy(); - - private UnboundedPoolLimitDeterminationStrategy() { } - - @Override - public boolean acquireCreationPermit(long acquireStartTime, TimeUnit timeUnit) { - return true; - } - - @Override - public int getAvailablePermits() { - return Integer.MAX_VALUE; - } - - @Override - public void releasePermit() { - // No Op, no limit. - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/events/Clock.java b/netty-http-rx/src/main/java/io/reactivex/netty/events/Clock.java deleted file mode 100644 index 4c29fc0..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/events/Clock.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.events; - -import io.reactivex.netty.RxNetty; - -import java.util.concurrent.TimeUnit; - -/** - * A simple utility to wrap start and end times of a call. - * - *

Thread Safety

- * - * This class is NOT threadsafe. - *

Memory overhead

- * - * One of the major concerns in publishing events is the object allocation overhead and having a Clock instance - * can attribute to such overheads. This is the reason why this class also provides static convenience methods to mark - * start and end of times to reduce some boiler plate code. - */ -public class Clock { - - /** - * The value returned by all static methods in this class, viz., - *
    -
  • {@link #newStartTime(TimeUnit)}
  • -
  • {@link #newStartTimeNanos()}
  • -
  • {@link #onEndNanos(long)}
  • -
  • {@link #onEnd(long, TimeUnit)}
  • -
- * after calling {@link RxNetty#disableEventPublishing()} - */ - public static final long SYSTEM_TIME_DISABLED_TIME = -1; - - private final long startTimeNanos = System.nanoTime(); - private long endTimeNanos = -1; - private long durationNanos = -1; - - /** - * Stops this clock. This method is idempotent, so, after invoking this method, the duration of the clock is - * immutable. Hence, you can call this method multiple times with no side-effects. - * - * @return The duration in nanoseconds for which the clock was running. - */ - public long stop() { - if (-1 != endTimeNanos) { - endTimeNanos = System.nanoTime(); - durationNanos = endTimeNanos - startTimeNanos; - } - return durationNanos; - } - - public long getStartTimeNanos() { - return startTimeNanos; - } - - public long getStartTime(TimeUnit targetUnit) { - return targetUnit.convert(startTimeNanos, TimeUnit.NANOSECONDS); - } - - /** - * Returns the duration for which this clock was running in nanoseconds. - * - * @return The duration for which this clock was running in nanoseconds. - * - * @throws IllegalStateException If the clock is not yet stopped. - */ - public long getDurationInNanos() { - if (isRunning()) { - throw new IllegalStateException("The clock is not yet stopped."); - } - return durationNanos; - } - - /** - * Returns the duration for which this clock was running in the given timeunit. - * - * @return The duration for which this clock was running in the given timeunit. - * - * @throws IllegalStateException If the clock is not yet stopped. - */ - public long getDuration(TimeUnit targetUnit) { - if (isRunning()) { - throw new IllegalStateException("The clock is not yet stopped."); - } - return targetUnit.convert(durationNanos, TimeUnit.NANOSECONDS); - } - - public boolean isRunning() { - return -1 != durationNanos; - } - - public static long newStartTimeNanos() { - return RxNetty.isEventPublishingDisabled() ? SYSTEM_TIME_DISABLED_TIME : System.nanoTime(); - } - - public static long newStartTime(TimeUnit timeUnit) { - if (RxNetty.isEventPublishingDisabled() ) { - return SYSTEM_TIME_DISABLED_TIME; - } - - if (TimeUnit.NANOSECONDS == timeUnit) { - return newStartTimeNanos(); - } - return timeUnit.convert(newStartTimeNanos(), TimeUnit.NANOSECONDS); - } - - public static long onEnd(long startTime, TimeUnit timeUnit) { - if (RxNetty.isEventPublishingDisabled() ) { - return SYSTEM_TIME_DISABLED_TIME; - } - if (TimeUnit.NANOSECONDS == timeUnit) { - return onEndNanos(startTime); - } - long startTimeNanos = TimeUnit.NANOSECONDS.convert(startTime, timeUnit); - return timeUnit.convert(onEndNanos(startTimeNanos), TimeUnit.NANOSECONDS); - } - - public static long onEndNanos(long startTimeNanos) { - if (RxNetty.isEventPublishingDisabled() ) { - return SYSTEM_TIME_DISABLED_TIME; - } - return System.nanoTime() - startTimeNanos; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/events/EventAttributeKeys.java b/netty-http-rx/src/main/java/io/reactivex/netty/events/EventAttributeKeys.java deleted file mode 100644 index 0bbec12..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/events/EventAttributeKeys.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.events; - -import io.netty.util.AttributeKey; -import io.reactivex.netty.channel.events.ConnectionEventListener; -import io.reactivex.netty.client.events.ClientEventListener; - -/** - * A set of {@link AttributeKey} that are used by the event infrastructure. - */ -public final class EventAttributeKeys { - - private EventAttributeKeys() { - } - - public static final AttributeKey EVENT_PUBLISHER = - AttributeKey.valueOf("rxnetty_client_event_publisher"); - public static final AttributeKey CLIENT_EVENT_LISTENER = - AttributeKey.valueOf("rxnetty_client_event_listener"); - public static final AttributeKey CONNECTION_EVENT_LISTENER = - AttributeKey.valueOf("rxnetty_client_conn_event_listener"); - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/events/EventListener.java b/netty-http-rx/src/main/java/io/reactivex/netty/events/EventListener.java deleted file mode 100644 index 2e9c1bf..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/events/EventListener.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.events; - -import java.util.concurrent.TimeUnit; - -/** - * A listener to subscribe to events published by an {@link EventSource} - */ -public interface EventListener { - - /** - * Marks the end of all event callbacks. No methods on this listener will ever be called once this method is called. - */ - void onCompleted(); - - /** - * Typically specific instances on {@link EventListener} will provide events that are fired by RxNetty, however, - * there may be cases, where a user would want to emit custom events. eg: Creating a custom protocol on top of an - * existing protocol like TCP, would perhaps require some additional events. In such a case, this callback can be - * utilized. - * - * @param event Event published. - * - * @see #onCustomEvent(Object, long, TimeUnit) - * @see #onCustomEvent(Object, Throwable) - * @see #onCustomEvent(Object, long, TimeUnit, Throwable) - */ - void onCustomEvent(Object event); - - /** - * Typically specific instances on {@link EventListener} will provide events that are fired by RxNetty, however, - * there may be cases, where a user would want to emit custom events. eg: Creating a custom protocol on top of an - * existing protocol like TCP, would perhaps require some additional events. In such a case, this callback can be - * utilized. - * - * One should use this overload as opposed to {@link #onCustomEvent(Object)} if the custom event need not be created - * per invocation but has to be associated with a duration. This is a simple optimization to reduce event creation - * overhead. - * - * @param event Event published. - * @param duration Duration associated with this event. The semantics of this duration is totally upto the published - * event. - * @param timeUnit Timeunit for the duration. - * - * @see #onCustomEvent(Object, long, TimeUnit) - * @see #onCustomEvent(Object, Throwable) - * @see #onCustomEvent(Object, long, TimeUnit, Throwable) - */ - void onCustomEvent(Object event, long duration, TimeUnit timeUnit); - - /** - * Typically specific instances on {@link EventListener} will provide events that are fired by RxNetty, however, - * there may be cases, where a user would want to emit custom events. eg: Creating a custom protocol on top of an - * existing protocol like TCP, would perhaps require some additional events. In such a case, this callback can be - * utilized. - * - * One should use this overload as opposed to {@link #onCustomEvent(Object)} if the custom event need not be created - * per invocation but has to be associated with an error. This is a simple optimization to reduce event creation - * overhead. - * - * @param event Event published. - * - * @see #onCustomEvent(Object, long, TimeUnit) - * @see #onCustomEvent(Object, Throwable) - * @see #onCustomEvent(Object, long, TimeUnit, Throwable) - */ - void onCustomEvent(Object event, Throwable throwable); - - /** - * Typically specific instances on {@link EventListener} will provide events that are fired by RxNetty, however, - * there may be cases, where a user would want to emit custom events. eg: Creating a custom protocol on top of an - * existing protocol like TCP, would perhaps require some additional events. In such a case, this callback can be - * utilized. - * - * One should use this overload as opposed to {@link #onCustomEvent(Object)} if the custom event need not be created - * per invocation but has to be associated with a duration and an error. This is a simple optimization to reduce - * event creation overhead. - * - * @param event Event published. - * @param duration Duration associated with this event. The semantics of this duration is totally upto the published - * event. - * @param timeUnit Timeunit for the duration. - * @param throwable Error associated with the event. - * - * @see #onCustomEvent(Object, long, TimeUnit) - * @see #onCustomEvent(Object, Throwable) - * @see #onCustomEvent(Object, long, TimeUnit, Throwable) - */ - void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/events/EventPublisher.java b/netty-http-rx/src/main/java/io/reactivex/netty/events/EventPublisher.java deleted file mode 100644 index 358d1f8..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/events/EventPublisher.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.events; - -/** - * A contract for any publisher of events. - */ -public interface EventPublisher { - - /** - * Returns {@code true} if event publishing is enabled. This is primarily used to short-circuit event publishing - * if the publishing is not enabled. Event publishing will be disabled if there are no active listeners or has - * been explicitly disabled using {@link io.reactivex.netty.RxNetty#disableEventPublishing()} - * - * @return {@code true} if event publishing is enabled. - */ - boolean publishingEnabled(); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/events/EventSource.java b/netty-http-rx/src/main/java/io/reactivex/netty/events/EventSource.java deleted file mode 100644 index a07c074..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/events/EventSource.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.events; - -import rx.Subscription; - -/** - * An event source to which {@link EventListener}s can subscribe to receive events. - */ -public interface EventSource { - - /** - * Subscribes the passed {@code listener} for events published by this source. - * - * @param listener Listener for events published by this source. - * - * @return Subscription, from which one can unsubscribe to stop receiving events. - */ - Subscription subscribe(T listener); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/events/ListenerInvocationException.java b/netty-http-rx/src/main/java/io/reactivex/netty/events/ListenerInvocationException.java deleted file mode 100644 index fbbda24..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/events/ListenerInvocationException.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.events; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -public class ListenerInvocationException extends RuntimeException { - - private Map exceptions; - private String message; - - private static final long serialVersionUID = -4381062024201397997L; - - @SuppressWarnings("rawtypes") - protected ListenerInvocationException() { - super("Metric event listener invocation failed."); - exceptions = new HashMap<>(); - message = super.getMessage(); - } - - protected void addException(EventListener listener, Throwable error) { - exceptions.put(listener, error); - } - - protected void finish() { - exceptions = Collections.unmodifiableMap(exceptions); - StringBuilder msgBuilder = new StringBuilder(getMessage()).append(". Errors: \n"); - for (Map.Entry exceptionEntry : exceptions.entrySet()) { - msgBuilder.append("Listener: "); - msgBuilder.append(exceptionEntry.getKey().getClass().getSimpleName()); - msgBuilder.append("\n Error:"); - ByteArrayOutputStream stackTraceStream = new ByteArrayOutputStream(); - exceptionEntry.getValue().printStackTrace(new PrintStream(stackTraceStream)); - msgBuilder.append(stackTraceStream.toString()); - } - message = msgBuilder.toString(); - } - - public Map getExceptions() { - return exceptions; - } - - @Override - public String getMessage() { - return message; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/events/ListenersHolder.java b/netty-http-rx/src/main/java/io/reactivex/netty/events/ListenersHolder.java deleted file mode 100644 index d4c2cd5..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/events/ListenersHolder.java +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.events; - -import io.reactivex.netty.RxNetty; -import java.util.logging.Level; -import java.util.logging.Logger; -import rx.Subscription; -import rx.exceptions.Exceptions; -import rx.functions.Action0; -import rx.functions.Action1; -import rx.functions.Action2; -import rx.functions.Action3; -import rx.functions.Action4; -import rx.functions.Action5; -import rx.subscriptions.CompositeSubscription; -import rx.subscriptions.Subscriptions; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.TimeUnit; - -/** - * A holder for storing {@link EventListener} providing utility methods for any {@link EventSource} implementation that - * requires storing and invoking listeners. - * - * @param Type of listener to store. - */ -public final class ListenersHolder implements EventSource, EventPublisher { - - private static final Logger logger = Logger.getLogger(ListenersHolder.class.getName()); - - private final CopyOnWriteArraySet> listeners; - - public ListenersHolder() { - listeners = new CopyOnWriteArraySet<>(); - } - - public ListenersHolder(ListenersHolder toCopy) { - - listeners = new CopyOnWriteArraySet<>(toCopy.listeners); - - for (final ListenerHolder holder : listeners) { - // Add the subscription to the existing holder, so that on unsubscribe, it is also removed from this list. - holder.subscription.add(Subscriptions.create(new Action0() { - @Override - public void call() { - listeners.remove(holder); - } - })); - } - } - - @Override - public Subscription subscribe(final T listener) { - final CompositeSubscription cs = new CompositeSubscription(); - - ListenerHolder.configureRemoval(cs, listener, listeners); - - final ListenerHolder holder = new ListenerHolder<>(listener, cs); - listeners.add(holder); - return cs; - } - - @Override - public boolean publishingEnabled() { - return !RxNetty.isEventPublishingDisabled() && !listeners.isEmpty(); - } - - public void dispose() { - ListenerInvocationException exception = null; - for (ListenerHolder listener : listeners) { - try { - listener.onCompleted(); - } catch (Throwable e) { - exception = handleListenerError(exception, listener, e); - } - } - - if (null != exception) { - exception.finish(); - throw exception; - } - } - - /** - * Invoke listeners with an action expressed by the passed {@code invocationAction}. This method does the necessary - * validations required for invoking a listener and also guards against a listener throwing exceptions on invocation. - * - * @param invocationAction The action to perform on all listeners. - */ - public void invokeListeners(Action1 invocationAction) { - ListenerInvocationException exception = null; - for (final ListenerHolder listener : listeners) { - if (!listener.subscription.isUnsubscribed()) { - try { - invocationAction.call(listener.delegate); - } catch (Throwable e) { - exception = handleListenerError(exception, listener, e); - } - } - } - - if (null != exception) { - exception.finish(); - /*Do not bubble event notification errors to the caller, event notifications are best effort.*/ - logger.log(Level.SEVERE, "Error occured while invoking event listeners.", exception); - } - } - - /** - * Invoke listeners with an action expressed by the passed {@code invocationAction}. This method does the necessary - * validations required for invoking a listener and also guards against a listener throwing exceptions on invocation. - * - * @param invocationAction The action to perform on all listeners. - * @param duration Duration. - * @param timeUnit Time unit for the duration. - */ - public void invokeListeners(Action3 invocationAction, long duration, TimeUnit timeUnit) { - ListenerInvocationException exception = null; - for (ListenerHolder listener : listeners) { - if (!listener.subscription.isUnsubscribed()) { - try { - invocationAction.call(listener.delegate, duration, timeUnit); - } catch (Throwable e) { - exception = handleListenerError(exception, listener, e); - } - } - } - - if (null != exception) { - exception.finish(); - /*Do not bubble event notification errors to the caller, event notifications are best effort.*/ - logger.log(Level.SEVERE, "Error occured while invoking event listeners.", exception); - } - } - - /** - * Invoke listeners with an action expressed by the passed {@code invocationAction}. This method does the necessary - * validations required for invoking a listener and also guards against a listener throwing exceptions on invocation. - * - * @param invocationAction The action to perform on all listeners. - * @param duration Duration. - * @param timeUnit Time unit for the duration. - * @param throwable An error. - */ - public void invokeListeners(Action4 invocationAction, long duration, - TimeUnit timeUnit, Throwable throwable) { - ListenerInvocationException exception = null; - for (ListenerHolder listener : listeners) { - if (!listener.subscription.isUnsubscribed()) { - try { - invocationAction.call(listener.delegate, duration, timeUnit, throwable); - } catch (Throwable e) { - exception = handleListenerError(exception, listener, e); - } - } - } - - if (null != exception) { - exception.finish(); - /*Do not bubble event notification errors to the caller, event notifications are best effort.*/ - logger.log(Level.SEVERE, "Error occured while invoking event listeners.", exception); - } - } - - /** - * Invoke listeners with an action expressed by the passed {@code invocationAction}. This method does the necessary - * validations required for invoking a listener and also guards against a listener throwing exceptions on invocation. - * - * @param invocationAction The action to perform on all listeners. - * @param duration Duration. - * @param timeUnit Time unit for the duration. - * @param arg Any arbitrary argument - */ - public void invokeListeners(Action4 invocationAction, long duration, - TimeUnit timeUnit, A arg) { - ListenerInvocationException exception = null; - for (ListenerHolder listener : listeners) { - if (!listener.subscription.isUnsubscribed()) { - try { - invocationAction.call(listener.delegate, duration, timeUnit, arg); - } catch (Throwable e) { - exception = handleListenerError(exception, listener, e); - } - } - } - - if (null != exception) { - exception.finish(); - /*Do not bubble event notification errors to the caller, event notifications are best effort.*/ - logger.log(Level.SEVERE, "Error occured while invoking event listeners.", exception); - } - } - - /** - * Invoke listeners with an action expressed by the passed {@code invocationAction}. This method does the necessary - * validations required for invoking a listener and also guards against a listener throwing exceptions on invocation. - * - * @param invocationAction The action to perform on all listeners. - * @param duration Duration. - * @param timeUnit Time unit for the duration. - * @param throwable An error. - * @param arg Any arbitrary argument - */ - public void invokeListeners(Action5 invocationAction, long duration, - TimeUnit timeUnit, Throwable throwable, A arg) { - ListenerInvocationException exception = null; - for (ListenerHolder listener : listeners) { - if (!listener.subscription.isUnsubscribed()) { - try { - invocationAction.call(listener.delegate, duration, timeUnit, throwable, arg); - } catch (Throwable e) { - exception = handleListenerError(exception, listener, e); - } - } - } - - if (null != exception) { - exception.finish(); - /*Do not bubble event notification errors to the caller, event notifications are best effort.*/ - logger.log(Level.SEVERE, "Error occured while invoking event listeners.", exception); - } - } - - /** - * Invoke listeners with an action expressed by the passed {@code invocationAction}. This method does the necessary - * validations required for invoking a listener and also guards against a listener throwing exceptions on invocation. - * - * @param invocationAction The action to perform on all listeners. - * @param arg Any arbitrary argument - */ - public void invokeListeners(Action2 invocationAction, A arg) { - ListenerInvocationException exception = null; - for (ListenerHolder listener : listeners) { - if (!listener.subscription.isUnsubscribed()) { - try { - invocationAction.call(listener.delegate, arg); - } catch (Throwable e) { - exception = handleListenerError(exception, listener, e); - } - } - } - - if (null != exception) { - exception.finish(); - /*Do not bubble event notification errors to the caller, event notifications are best effort.*/ - logger.log(Level.SEVERE, "Error occured while invoking event listeners.", exception); - } - } - - /** - * Invoke listeners with an action expressed by the passed {@code invocationAction}. This method does the necessary - * validations required for invoking a listener and also guards against a listener throwing exceptions on invocation. - * - * @param invocationAction The action to perform on all listeners. - * @param throwable An error. - * @param arg Any arbitrary argument - */ - public void invokeListeners(Action3 invocationAction, Throwable throwable, A arg) { - ListenerInvocationException exception = null; - for (ListenerHolder listener : listeners) { - if (!listener.subscription.isUnsubscribed()) { - try { - invocationAction.call(listener.delegate, throwable, arg); - } catch (Throwable e) { - exception = handleListenerError(exception, listener, e); - } - } - } - - if (null != exception) { - exception.finish(); - /*Do not bubble event notification errors to the caller, event notifications are best effort.*/ - logger.log(Level.SEVERE, "Error occured while invoking event listeners.", exception); - } - } - - private ListenerInvocationException handleListenerError(ListenerInvocationException exception, - ListenerHolder listener, Throwable e) { - Exceptions.throwIfFatal(e); - if (null == exception) { - exception = new ListenerInvocationException(); - } - exception.addException(listener.delegate, e); - return exception; - } - - public ListenersHolder copy() { - return new ListenersHolder<>(this); - } - - /*Visible for testing*/Collection getAllListeners() { - final Collection toReturn = new ArrayList<>(); - for (ListenerHolder listener : listeners) { - toReturn.add(listener.delegate); - } - return toReturn; - } - - /*Visible for testing*/CopyOnWriteArraySet> getActualListenersList() { - return listeners; - } - - public void subscribeAllTo(EventSource lazySource) { - for (ListenerHolder listener : listeners) { - listener.subscription.add(lazySource.subscribe(listener.delegate)); - } - } - - private static class ListenerHolder implements EventListener { - - private static final CompositeSubscription EMPTY_SUB_FOR_REMOVAL = new CompositeSubscription(); - - private final T delegate; - private final CompositeSubscription subscription; - - public ListenerHolder(T delegate, CompositeSubscription subscription) { - this.delegate = delegate; - this.subscription = subscription; - } - - @Override - public void onCompleted() { - if (!subscription.isUnsubscribed()) { - try { - delegate.onCompleted(); - } finally { - subscription.unsubscribe(); - } - } - } - - @Override - public void onCustomEvent(Object event) { } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { } - - public static ListenerHolder forRemoval(X listenerToRemove) { - return new ListenerHolder<>(listenerToRemove, EMPTY_SUB_FOR_REMOVAL); - } - - public static void configureRemoval(CompositeSubscription cs, - final X listenerToRemove, - final CopyOnWriteArraySet> removeFrom) { - cs.add(Subscriptions.create(new Action0() { - @Override - public void call() { - /** - * Why do we add {@link ListenerHolder} but remove {@link X}? - * Since {@link ListenerHolder} requires the associated {@link Subscription}, and then - * {@link Subscription} will require the {@link ListenerHolder}, there will be a circular dependency. - * - * Instead, by having {@link ListenerHolder} implement equals/hashcode to only look for the - * enclosing {@link X} instance, it is possible to add {@link ListenerHolder} but remove {@link X} - */ - removeFrom.remove(forRemoval(listenerToRemove)); - } - })); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof ListenerHolder)) { - return false; - } - - @SuppressWarnings("rawtypes") - ListenerHolder that = (ListenerHolder) o; - - return delegate.equals(that.delegate); - - } - - @Override - public int hashCode() { - return delegate.hashCode(); - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/events/internal/SafeEventListener.java b/netty-http-rx/src/main/java/io/reactivex/netty/events/internal/SafeEventListener.java deleted file mode 100644 index b48b89d..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/events/internal/SafeEventListener.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.events.internal; - -import io.reactivex.netty.events.EventListener; - -/** - * A marker interface to indicate that the {@link EventListener} is safe to be invoked multiple times but the wrapped - * listener will only be invoked once. - */ -public interface SafeEventListener extends EventListener { -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/internal/ExecuteInEventloopAction.java b/netty-http-rx/src/main/java/io/reactivex/netty/internal/ExecuteInEventloopAction.java deleted file mode 100644 index 26a5aea..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/internal/ExecuteInEventloopAction.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.internal; - -import io.netty.channel.Channel; -import rx.functions.Action0; - -public abstract class ExecuteInEventloopAction implements Action0, Runnable { - - private final Channel channel; - - protected ExecuteInEventloopAction(Channel channel) { - this.channel = channel; - } - - @Override - public void call() { - if (channel.eventLoop().inEventLoop()) { - run(); - } else { - channel.eventLoop().execute(this); - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/internal/InternalReadTimeoutHandler.java b/netty-http-rx/src/main/java/io/reactivex/netty/internal/InternalReadTimeoutHandler.java deleted file mode 100644 index 83bc01f..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/internal/InternalReadTimeoutHandler.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.internal; - -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.handler.timeout.ReadTimeoutException; -import io.netty.handler.timeout.ReadTimeoutHandler; -import io.reactivex.netty.client.ClientConnectionToChannelBridge.PooledConnectionReleaseEvent; -import java.util.logging.Level; -import java.util.logging.Logger; - -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -/** - * A copy of netty's {@link ReadTimeoutHandler}. This is required because {@link ReadTimeoutHandler} does not allow - * reuse in the same pipeline, which is required for connection pooling. - * See issue https://github.com/ReactiveX/RxNetty/issues/344 - */ -public class InternalReadTimeoutHandler extends ChannelDuplexHandler { - - private static final Logger logger = Logger.getLogger(InternalReadTimeoutHandler.class.getName()); - - private static final long MIN_TIMEOUT_NANOS = TimeUnit.MILLISECONDS.toNanos(1); - - private final long timeoutNanos; - - private volatile ScheduledFuture timeout; - private volatile long lastReadTime; - - private enum State { - Created, - Active, - Paused, - Destroyed - } - - private volatile State state = State.Created; - - private boolean closed; - - /** - * Creates a new instance. - * - * @param timeout - * read timeout - * @param unit - * the {@link TimeUnit} of {@code timeout} - */ - public InternalReadTimeoutHandler(long timeout, TimeUnit unit) { - if (unit == null) { - throw new NullPointerException("unit"); - } - - if (timeout <= 0) { - timeoutNanos = 0; - } else { - timeoutNanos = Math.max(unit.toNanos(timeout), MIN_TIMEOUT_NANOS); - } - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - if (ctx.channel().isActive() && ctx.channel().isRegistered()) { - // channelActive() event has been fired already, which means this.channelActive() will - // not be invoked. We have to scheduleAfresh here instead. - scheduleAfresh(ctx); - } - - // channelActive() event has not been fired yet. this.channelActive() will be invoked - // and initialization will occur there. - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - destroy(); - } - - @Override - public void channelRegistered(ChannelHandlerContext ctx) throws Exception { - // Initialize early if channel is active already. - if (ctx.channel().isActive()) { - scheduleAfresh(ctx); - } - super.channelRegistered(ctx); - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - // This method will be invoked only if this handler was added - // before channelActive() event is fired. If a user adds this handler - // after the channelActive() event, scheduleAfresh() will be called by beforeAdd(). - scheduleAfresh(ctx); - super.channelActive(ctx); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - destroy(); - super.channelInactive(ctx); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - lastReadTime = System.nanoTime(); - ctx.fireChannelRead(msg); - } - - @Override - public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - if (State.Paused == state) { - // Add the timeout handler when write is complete. - promise.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (State.Paused == state) { - /* - * Multiple writes can all add a listener, till it is active again (on write success), so it is - * required to only schedule next when the state is actually paused. - */ - scheduleAfresh(ctx); - } - } - }); - } - - super.write(ctx, msg, promise); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof PooledConnectionReleaseEvent) { - cancelTimeoutSchedule(ctx); - } - super.userEventTriggered(ctx, evt); - } - - private void cancelTimeoutSchedule(ChannelHandlerContext ctx) { - assert ctx.channel().eventLoop().inEventLoop(); /*should only be called from the owner eventloop*/ - if (State.Active == state) { - state = State.Paused; - timeout.cancel(false); - } - } - - private void scheduleAfresh(ChannelHandlerContext ctx) { - // Avoid the case where destroy() is called before scheduling timeouts. - // See: https://github.com/netty/netty/issues/143 - switch (state) { - case Created: - break; - case Active: - return; - case Paused: - break; - case Destroyed: - logger.log(Level.WARNING, "Not scheduling next read timeout task as the channel handler is removed."); - return; - } - - state = State.Active; - - lastReadTime = System.nanoTime(); - if (timeoutNanos > 0) { - timeout = _scheduleNextTask(ctx, new ReadTimeoutTask(ctx), timeoutNanos); - } - } - - private ScheduledFuture _scheduleNextTask(ChannelHandlerContext ctx, ReadTimeoutTask task, long timeoutNanos) { - try { - return ctx.executor().schedule(task, timeoutNanos, TimeUnit.NANOSECONDS); - } catch (Exception e) { - logger.log(Level.SEVERE, "Failed to schedule read timeout task. Read timeout will not work on channel: " - + ctx.channel(), e); - throw e; - } - } - - private void destroy() { - state = State.Destroyed; - - if (timeout != null) { - timeout.cancel(false); - timeout = null; - } - } - - /** - * Is called when a read timeout was detected. - */ - protected void readTimedOut(ChannelHandlerContext ctx) throws Exception { - if (!closed) { - ctx.fireExceptionCaught(ReadTimeoutException.INSTANCE); - ctx.close(); - closed = true; - } - } - - private final class ReadTimeoutTask implements Runnable { - - private final ChannelHandlerContext ctx; - - ReadTimeoutTask(ChannelHandlerContext ctx) { - this.ctx = ctx; - } - - @Override - public void run() { - if (!ctx.channel().isOpen()) { - return; - } - - long currentTime = System.nanoTime(); - long nextDelay = timeoutNanos - (currentTime - lastReadTime); - if (nextDelay <= 0) { - // Read timed out - set a new timeout and notify the callback. - timeout = ctx.executor().schedule(this, timeoutNanos, TimeUnit.NANOSECONDS); - try { - readTimedOut(ctx); - } catch (Throwable t) { - ctx.fireExceptionCaught(t); - } - } else { - // Read occurred before the timeout - set a new timeout with shorter delay. - timeout = ctx.executor().schedule(this, nextDelay, TimeUnit.NANOSECONDS); - } - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/internal/VoidToAnythingCast.java b/netty-http-rx/src/main/java/io/reactivex/netty/internal/VoidToAnythingCast.java deleted file mode 100644 index 0faa569..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/internal/VoidToAnythingCast.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.internal; - -import rx.Observable; -import rx.functions.Func1; - -/** - * A function to be used in place of {@link Observable#cast(Class)} to support nested generics. - * - * @param Target type. - */ -public class VoidToAnythingCast implements Func1 { - - @Override - public T call(Void aVoid) { - return null; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/CookiesHolder.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/CookiesHolder.java deleted file mode 100644 index 8fdcbca..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/CookiesHolder.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http; - -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.cookie.ClientCookieDecoder; -import io.netty.handler.codec.http.cookie.Cookie; -import io.netty.handler.codec.http.cookie.ServerCookieDecoder; -import io.netty.util.AsciiString; - -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; -/** - * A holder of cookies parsed from the Http headers. - */ -public class CookiesHolder { - - private final HttpHeaders nettyHeaders; - private final AsciiString cookiesHeaderName; - private final boolean isClientDecoder; - private Map> allCookies; - private boolean cookiesParsed; - - private CookiesHolder(HttpHeaders nettyHeaders, AsciiString cookiesHeaderName, boolean isClientDecoder) { - this.nettyHeaders = nettyHeaders; - this.cookiesHeaderName = cookiesHeaderName; - this.isClientDecoder = isClientDecoder; - allCookies = Collections.emptyMap(); - } - - public Map> getAllCookies() { - return _parseIfNeededAndGet(); - } - - public static CookiesHolder newClientResponseHolder(HttpHeaders headers) { - return new CookiesHolder(headers, SET_COOKIE, true); - } - - public static CookiesHolder newServerRequestHolder(HttpHeaders headers) { - return new CookiesHolder(headers, COOKIE, false); - } - - private synchronized Map> _parseIfNeededAndGet() { - if (cookiesParsed) { // This method is synchronized, a memory barrier for this variable to be refreshed. - return allCookies; - } - List allCookieHeaders = nettyHeaders.getAll(cookiesHeaderName); - Map> cookies = new HashMap<>(); - for (String aCookieHeader : allCookieHeaders) { - Set decode; - if (isClientDecoder) { - Cookie decoded = ClientCookieDecoder.STRICT.decode(aCookieHeader); - Set existingCookiesOfName = cookies.get(decoded.name()); - if (null == existingCookiesOfName) { - existingCookiesOfName = new HashSet<>(); - cookies.put(decoded.name(), existingCookiesOfName); - } - existingCookiesOfName.add(decoded); - } else { - decode = ServerCookieDecoder.STRICT.decode(aCookieHeader); - for (Cookie cookie : decode) { - Set existingCookiesOfName = cookies.get(cookie.name()); - if (null == existingCookiesOfName) { - existingCookiesOfName = new HashSet<>(); - cookies.put(cookie.name(), existingCookiesOfName); - } - existingCookiesOfName.add(cookie); - } - } - } - allCookies = Collections.unmodifiableMap(cookies); - cookiesParsed = true; - return allCookies; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/HttpHandlerNames.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/HttpHandlerNames.java deleted file mode 100644 index 2cb5e11..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/HttpHandlerNames.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http; - -/** - * A list of all handler names added by for HTTP. This is just to ensure consistency in naming. - */ -public enum HttpHandlerNames { - - HttpClientCodec("http-client-codec"), - HttpServerDecoder("http-server-request-decoder"), - HttpServerEncoder("http-server-response-encoder"), - WsServerDecoder("ws-server-request-decoder"), - WsServerEncoder("ws-server-response-encoder"), - WsServerUpgradeHandler("ws-server-upgrade-handler"), - WsClientDecoder("ws-client-request-decoder"), - WsClientEncoder("ws-client-response-encoder"), - WsClientUpgradeHandler("ws-client-upgrade-handler"), - SseClientCodec("sse-client-codec"), - SseServerCodec("sse-server-codec"), - ; - - private final String name; - - HttpHandlerNames(String name) { - this.name = qualify(name); - } - - public String getName() { - return name; - } - - private static String qualify(String name) { - return "_rx_netty_" + name; - - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/TrailingHeaders.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/TrailingHeaders.java deleted file mode 100644 index bdf59ec..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/TrailingHeaders.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http; - -import io.netty.handler.codec.http.DefaultLastHttpContent; -import io.netty.handler.codec.http.LastHttpContent; - -import java.util.List; - -/** - * A mutable stateful entity containing trailing headers. - * - * This class is not thread safe - */ -public class TrailingHeaders { - - private final LastHttpContent lastHttpContent; - - public TrailingHeaders() { - lastHttpContent = LastHttpContent.EMPTY_LAST_CONTENT; - } - - public TrailingHeaders(LastHttpContent lastHttpContent) { - this.lastHttpContent = lastHttpContent; - } - - /** - * Adds an HTTP trailing header with the passed {@code name} and {@code value} to this request. - * - * @param name Name of the header. - * @param value Value for the header. - * - * @return {@code this}. - */ - public TrailingHeaders addHeader(CharSequence name, Object value) { - lastHttpContent.trailingHeaders().add(name, value); - return this; - } - - /** - * Adds an HTTP trailing header with the passed {@code name} and {@code values} to this request. - * - * @param name Name of the header. - * @param values Values for the header. - * - * @return {@code this}. - */ - public TrailingHeaders addHeader(CharSequence name, Iterable values) { - lastHttpContent.trailingHeaders().add(name, values); - return this; - } - - /** - * Overwrites the current value, if any, of the passed trailing header to the passed value for this request. - * - * @param name Name of the header. - * @param value Value of the header. - * - * @return {@code this}. - */ - public TrailingHeaders setHeader(CharSequence name, Object value) { - lastHttpContent.trailingHeaders().set(name, value); - return this; - } - - /** - * Overwrites the current value, if any, of the passed trailing header to the passed values for this request. - * - * @param name Name of the header. - * @param values Values of the header. - * - * @return {@code this}. - */ - public TrailingHeaders setHeader(CharSequence name, Iterable values) { - lastHttpContent.trailingHeaders().set(name, values); - return this; - } - - /** - * Returns the value of a header with the specified name. If there are more than one values for the specified name, - * the first value is returned. - * - * @param name The name of the header to search - * - * @return The first header value or {@code null} if there is no such header - */ - public String getHeader(CharSequence name) { - return lastHttpContent.trailingHeaders().get(name); - } - - /** - * Returns the values of headers with the specified name - * - * @param name The name of the headers to search - * - * @return A {@link List} of header values which will be empty if no values are found - */ - public List getAllHeaderValues(CharSequence name) { - return lastHttpContent.trailingHeaders().getAll(name); - } - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClient.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClient.java deleted file mode 100644 index 9f875d0..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClient.java +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslHandler; -import io.netty.util.concurrent.EventExecutorGroup; -import io.reactivex.netty.client.ChannelProviderFactory; -import io.reactivex.netty.client.ConnectionProviderFactory; -import io.reactivex.netty.client.Host; -import io.reactivex.netty.ssl.SslCodec; -import rx.Observable; -import rx.functions.Action1; -import rx.functions.Func0; -import rx.functions.Func1; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.TrustManagerFactory; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.concurrent.TimeUnit; - -/** - * An HTTP client for executing HTTP requests. - * - *

Immutability

- * An instance of this client is immutable and all mutations produce a new client instance. For this reason it is - * recommended that the mutations are done during client creation and not during request processing to avoid repeated - * object creation overhead. - * - * @param The type of the content of request. - * @param The type of the content of response. - */ -public abstract class HttpClient extends InterceptingHttpClient { - - /** - * Creates a new client instances, inheriting all configurations from this client and adding the passed read timeout - * for all requests created by the newly created client instance. - * - * @param timeOut Read timeout duration. - * @param timeUnit Timeunit for the timeout. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient readTimeOut(int timeOut, TimeUnit timeUnit); - - /** - * Creates a new client instances, inheriting all configurations from this client and adding a - * {@link ChannelOption} for the connections created by the newly created client instance. - * - * @param option Option to add. - * @param value Value for the option. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient channelOption(ChannelOption option, T value); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this client. The specified - * handler is added at the first position of the pipeline as specified by - * {@link ChannelPipeline#addFirst(String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param name Name of the handler. - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient addChannelHandlerFirst(String name, Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this client. The specified - * handler is added at the first position of the pipeline as specified by - * {@link ChannelPipeline#addFirst(EventExecutorGroup, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} - * methods - * @param name the name of the handler to append - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient addChannelHandlerFirst(EventExecutorGroup group, String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this client. The specified - * handler is added at the last position of the pipeline as specified by - * {@link ChannelPipeline#addLast(String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param name Name of the handler. - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient addChannelHandlerLast(String name, Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this client. The specified - * handler is added at the last position of the pipeline as specified by - * {@link ChannelPipeline#addLast(EventExecutorGroup, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} - * methods - * @param name the name of the handler to append - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient addChannelHandlerLast(EventExecutorGroup group, String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this client. The specified - * handler is added before an existing handler with the passed {@code baseName} in the pipeline as specified by - * {@link ChannelPipeline#addBefore(String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param baseName the name of the existing handler - * @param name Name of the handler. - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient addChannelHandlerBefore(String baseName, String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this client. The specified - * handler is added before an existing handler with the passed {@code baseName} in the pipeline as specified by - * {@link ChannelPipeline#addBefore(EventExecutorGroup, String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} - * methods - * @param baseName the name of the existing handler - * @param name the name of the handler to append - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient addChannelHandlerBefore(EventExecutorGroup group, String baseName, - String name, Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this client. The specified - * handler is added after an existing handler with the passed {@code baseName} in the pipeline as specified by - * {@link ChannelPipeline#addAfter(String, String, ChannelHandler)} - * - * @param baseName the name of the existing handler - * @param name Name of the handler. - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient addChannelHandlerAfter(String baseName, String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this client. The specified - * handler is added after an existing handler with the passed {@code baseName} in the pipeline as specified by - * {@link ChannelPipeline#addAfter(EventExecutorGroup, String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} - * methods - * @param baseName the name of the existing handler - * @param name the name of the handler to append - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient addChannelHandlerAfter(EventExecutorGroup group, String baseName, - String name, Func0 handlerFactory); - - /** - * Creates a new client instances, inheriting all configurations from this client and using the passed - * action to configure all the connections created by the newly created client instance. - * - * @param pipelineConfigurator Action to configure {@link ChannelPipeline}. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient pipelineConfigurator(Action1 pipelineConfigurator); - - /** - * Creates a new client instance, inheriting all configurations from this client and using the passed - * {@code sslEngineFactory} for all secured connections created by the newly created client instance. - * - * If the {@link SSLEngine} instance can be statically, created, {@link #secure(SSLEngine)} can be used. - * - * @param sslEngineFactory Factory for all secured connections created by the newly created client instance. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient secure(Func1 sslEngineFactory); - - /** - * Creates a new client instance, inheriting all configurations from this client and using the passed - * {@code sslEngine} for all secured connections created by the newly created client instance. - * - * If the {@link SSLEngine} instance can not be statically, created, {@link #secure(Func1)} )} can be used. - * - * @param sslEngine {@link SSLEngine} for all secured connections created by the newly created client instance. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient secure(SSLEngine sslEngine); - - /** - * Creates a new client instance, inheriting all configurations from this client and using the passed - * {@code sslCodec} for all secured connections created by the newly created client instance. - * - * This is required only when the {@link SslHandler} used by {@link SslCodec} is to be modified before adding to - * the {@link ChannelPipeline}. For most of the cases, {@link #secure(Func1)} or {@link #secure(SSLEngine)} will be - * enough. - * - * @param sslCodec {@link SslCodec} for all secured connections created by the newly created client instance. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient secure(SslCodec sslCodec); - - /** - * Creates a new client instance, inheriting all configurations from this client and using a trust-all - * {@link TrustManagerFactory}for all secured connections created by the newly created client instance. - * - * This is only for testing and should not be used for real production clients. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient unsafeSecure(); - - /** - * Creates a new client instances, inheriting all configurations from this client and enabling wire logging at the - * passed level for the newly created client instance. - * - * @param wireLoggingLevel Logging level at which the wire logs will be logged. The wire logging will only be done if - * logging is enabled at this level for {@link LoggingHandler} - * - * @return A new {@link HttpClient} instance. - * - * @deprecated Use {@link #enableWireLogging(String, LogLevel)} instead. - */ - @Deprecated - public abstract HttpClient enableWireLogging(LogLevel wireLoggingLevel); - - /** - * Creates a new client instances, inheriting all configurations from this client and enabling wire logging at the - * passed level for the newly created client instance. - * - * @param name Name of the logger that can be used to control the logging dynamically. - * @param wireLoggingLevel Logging level at which the wire logs will be logged. The wire logging will only be done if - * logging is enabled at this level for {@link LoggingHandler} - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient enableWireLogging(String name, LogLevel wireLoggingLevel); - - /** - * Creates a new client instance, inheriting all configurations from this client and using the passed - * {@code providerFactory}. - * - * @param providerFactory Channel provider factory. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient channelProvider(ChannelProviderFactory providerFactory); - - /** - * Creates a new HTTP client instance with the passed host and port for the target server. - * - * @param host Hostname for the target server. - * @param port Port for the target server. - * - * @return A new {@code HttpClient} instance. - */ - public static HttpClient newClient(String host, int port) { - return newClient(new InetSocketAddress(host, port)); - } - - /** - * Creates a new HTTP client instance with the passed address of the target server. - * - * @param serverAddress Socket address for the target server. - * - * @return A new {@code HttpClient} instance. - */ - public static HttpClient newClient(SocketAddress serverAddress) { - return HttpClientImpl.create(serverAddress); - } - - /** - * Creates a new HTTP client instance using the supplied connection provider. - * - * @param providerFactory {@link ConnectionProviderFactory} for the client. - * @param hostStream Stream of hosts for the client. - * - * @return A new {@code HttpClient} instance. - */ - public static HttpClient newClient(ConnectionProviderFactory providerFactory, - Observable hostStream) { - return HttpClientImpl.create(providerFactory, hostStream); - } - - /** - * Creates a new client instances, inheriting all configurations from this client and following the passed number of - * max HTTP redirects. - * - * @param maxRedirects Maximum number of redirects to follow for any request. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient followRedirects(int maxRedirects); - - /** - * Creates a new client instances, inheriting all configurations from this client and enabling/disabling redirects - * for all requests created by the newly created client instance. - * - * @param follow {@code true} to follow redirects. {@code false} to disable any redirects. - * - * @return A new {@link HttpClient} instance. - */ - public abstract HttpClient followRedirects(boolean follow); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientImpl.java deleted file mode 100644 index 79b3280..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientImpl.java +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.http.HttpClientCodec; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.logging.LogLevel; -import io.netty.util.concurrent.EventExecutorGroup; -import io.reactivex.netty.client.ChannelProviderFactory; -import io.reactivex.netty.client.ConnectionProvider; -import io.reactivex.netty.client.ConnectionProviderFactory; -import io.reactivex.netty.client.Host; -import io.reactivex.netty.client.HostConnector; -import io.reactivex.netty.protocol.http.HttpHandlerNames; -import io.reactivex.netty.protocol.http.client.events.HttpClientEventPublisher; -import io.reactivex.netty.protocol.http.client.events.HttpClientEventsListener; -import io.reactivex.netty.protocol.http.client.internal.HttpChannelProviderFactory; -import io.reactivex.netty.protocol.http.client.internal.HttpClientRequestImpl; -import io.reactivex.netty.protocol.http.client.internal.HttpClientToConnectionBridge; -import io.reactivex.netty.protocol.http.client.internal.Redirector; -import io.reactivex.netty.protocol.http.ws.client.Ws7To13UpgradeHandler; -import io.reactivex.netty.protocol.tcp.client.TcpClient; -import io.reactivex.netty.protocol.tcp.client.TcpClientImpl; -import io.reactivex.netty.ssl.SslCodec; -import rx.Observable; -import rx.Subscription; -import rx.functions.Action1; -import rx.functions.Func0; -import rx.functions.Func1; - -import javax.net.ssl.SSLEngine; -import java.net.SocketAddress; -import java.util.concurrent.TimeUnit; - -import static io.reactivex.netty.protocol.http.client.internal.HttpClientRequestImpl.*; - -public final class HttpClientImpl extends HttpClient { - - private final TcpClient> client; - private final int maxRedirects; - private final HttpClientEventPublisher clientEventPublisher; - private final RequestProvider requestProvider; - - private HttpClientImpl(final TcpClient> client, final int maxRedirects, - HttpClientEventPublisher clientEventPublisher) { - this.client = client; - this.maxRedirects = maxRedirects; - this.clientEventPublisher = clientEventPublisher; - requestProvider = new RequestProvider() { - @Override - public HttpClientRequest createRequest(HttpVersion version, HttpMethod method, String uri) { - return HttpClientRequestImpl.create(version, method, uri, client, maxRedirects); - } - }; - } - - @Override - public HttpClientRequest createGet(String uri) { - return createRequest(HttpMethod.GET, uri); - } - - @Override - public HttpClientRequest createPost(String uri) { - return createRequest(HttpMethod.POST, uri); - } - - @Override - public HttpClientRequest createPut(String uri) { - return createRequest(HttpMethod.PUT, uri); - } - - @Override - public HttpClientRequest createDelete(String uri) { - return createRequest(HttpMethod.DELETE, uri); - } - - @Override - public HttpClientRequest createHead(String uri) { - return createRequest(HttpMethod.HEAD, uri); - } - - @Override - public HttpClientRequest createOptions(String uri) { - return createRequest(HttpMethod.OPTIONS, uri); - } - - @Override - public HttpClientRequest createPatch(String uri) { - return createRequest(HttpMethod.PATCH, uri); - } - - @Override - public HttpClientRequest createTrace(String uri) { - return createRequest(HttpMethod.TRACE, uri); - } - - @Override - public HttpClientRequest createConnect(String uri) { - return createRequest(HttpMethod.CONNECT, uri); - } - - @Override - public HttpClientRequest createRequest(HttpMethod method, String uri) { - return createRequest(HttpVersion.HTTP_1_1, method, uri); - } - - @Override - public HttpClientRequest createRequest(HttpVersion version, HttpMethod method, String uri) { - return requestProvider.createRequest(version, method, uri); - } - - @Override - public HttpClientInterceptorChain intercept() { - return new HttpClientInterceptorChainImpl<>(requestProvider, clientEventPublisher); - } - - @Override - public HttpClientImpl readTimeOut(int timeOut, TimeUnit timeUnit) { - return _copy(client.readTimeOut(timeOut, timeUnit), maxRedirects); - } - - @Override - public HttpClientImpl followRedirects(int maxRedirects) { - return _copy(client, maxRedirects); - } - - @Override - public HttpClientImpl followRedirects(boolean follow) { - return _copy(client, follow ? Redirector.DEFAULT_MAX_REDIRECTS : NO_REDIRECTS); - } - - @Override - public HttpClientImpl channelOption(ChannelOption option, T value) { - return _copy(client.channelOption(option, value), maxRedirects); - } - - @Override - public HttpClientImpl addChannelHandlerFirst(String name, Func0 handlerFactory) { - return _copy(HttpClientImpl.castClient(client.addChannelHandlerFirst(name, handlerFactory)), - maxRedirects); - } - - @Override - public HttpClientImpl addChannelHandlerFirst(EventExecutorGroup group, String name, - Func0 handlerFactory) { - return _copy(HttpClientImpl.castClient(client.addChannelHandlerFirst(group, name, handlerFactory)), - maxRedirects); - } - - @Override - public HttpClientImpl addChannelHandlerLast(String name, Func0 handlerFactory) { - return _copy(HttpClientImpl.castClient(client.addChannelHandlerLast(name, handlerFactory)), - maxRedirects); - } - - @Override - public HttpClientImpl addChannelHandlerLast(EventExecutorGroup group, String name, - Func0 handlerFactory) { - return _copy(HttpClientImpl.castClient(client.addChannelHandlerLast(group, name, handlerFactory)), - maxRedirects); - } - - @Override - public HttpClientImpl addChannelHandlerBefore(String baseName, String name, - Func0 handlerFactory) { - return _copy(HttpClientImpl.castClient(client.addChannelHandlerBefore(baseName, name, handlerFactory)), - maxRedirects); - } - - @Override - public HttpClientImpl addChannelHandlerBefore(EventExecutorGroup group, String baseName, String name, - Func0 handlerFactory) { - return _copy(HttpClientImpl.castClient(client.addChannelHandlerBefore(group, baseName, name, - handlerFactory)), - maxRedirects); - } - - @Override - public HttpClientImpl addChannelHandlerAfter(String baseName, String name, - Func0 handlerFactory) { - return _copy(HttpClientImpl.castClient(client.addChannelHandlerAfter(baseName, name, handlerFactory)), - maxRedirects); - } - - @Override - public HttpClientImpl addChannelHandlerAfter(EventExecutorGroup group, String baseName, String name, - Func0 handlerFactory) { - return _copy(HttpClientImpl.castClient(client.addChannelHandlerAfter(group, baseName, name, - handlerFactory)), - maxRedirects); - } - - @Override - public HttpClientImpl pipelineConfigurator(Action1 pipelineConfigurator) { - return _copy(HttpClientImpl.castClient(client.pipelineConfigurator(pipelineConfigurator)), - maxRedirects); - } - - @Override - public HttpClientImpl secure(Func1 sslEngineFactory) { - return _copy(client.secure(sslEngineFactory), maxRedirects); - } - - @Override - public HttpClientImpl secure(SSLEngine sslEngine) { - return _copy(client.secure(sslEngine), maxRedirects); - } - - @Override - public HttpClientImpl secure(SslCodec sslCodec) { - return _copy(client.secure(sslCodec), maxRedirects); - } - - @Override - public HttpClientImpl unsafeSecure() { - return _copy(client.unsafeSecure(), maxRedirects); - } - - @Override - @Deprecated - public HttpClientImpl enableWireLogging(LogLevel wireLoggingLevel) { - return _copy(client.enableWireLogging(wireLoggingLevel), maxRedirects); - } - - @Override - public HttpClient enableWireLogging(String name, LogLevel wireLoggingLevel) { - return _copy(client.enableWireLogging(name, wireLoggingLevel), maxRedirects); - } - - @Override - public HttpClientImpl channelProvider(ChannelProviderFactory providerFactory) { - return _copy(client.channelProvider(new HttpChannelProviderFactory(clientEventPublisher, providerFactory)), - maxRedirects); - } - - @Override - public Subscription subscribe(HttpClientEventsListener listener) { - return clientEventPublisher.subscribe(listener); - } - - public static HttpClient create(final ConnectionProviderFactory providerFactory, - Observable hostStream) { - ConnectionProviderFactory cpf = new ConnectionProviderFactory() { - @Override - public ConnectionProvider newProvider(Observable> hosts) { - return providerFactory.newProvider(hosts.map( - new Func1, HostConnector>() { - @Override - public HostConnector call(HostConnector hc) { - HttpClientEventPublisher hcep = new HttpClientEventPublisher(); - hc.subscribe(hcep); - return new HostConnector<>(hc.getHost(), hc.getConnectionProvider(), hcep, hcep, hcep); - } - })); - } - }; - return _newClient(TcpClientImpl.create(cpf, hostStream)); - } - - public static HttpClient create(SocketAddress socketAddress) { - return _newClient(TcpClientImpl.create(socketAddress)); - } - - private static HttpClient _newClient(TcpClient tcpClient) { - - HttpClientEventPublisher clientEventPublisher = new HttpClientEventPublisher(); - - TcpClient> client = - tcpClient.>pipelineConfigurator(new Action1() { - @Override - public void call(ChannelPipeline pipeline) { - pipeline.addLast(HttpHandlerNames.HttpClientCodec.getName(), new HttpClientCodec()); - pipeline.addLast(new HttpClientToConnectionBridge<>()); - pipeline.addLast(HttpHandlerNames.WsClientUpgradeHandler.getName(), - new Ws7To13UpgradeHandler()); - } - }).channelProvider(new HttpChannelProviderFactory(clientEventPublisher)); - - client.subscribe(clientEventPublisher); - - return new HttpClientImpl<>(client, NO_REDIRECTS, clientEventPublisher); - } - - @SuppressWarnings("unchecked") - private static TcpClient> castClient(TcpClient rawTypes) { - return (TcpClient>) rawTypes; - } - - private HttpClientImpl _copy(TcpClient> newClient, int maxRedirects) { - return new HttpClientImpl<>(newClient, maxRedirects, clientEventPublisher); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientInterceptorChain.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientInterceptorChain.java deleted file mode 100644 index 418fddb..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientInterceptorChain.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -/** - * Interceptor chain for {@link HttpClient}, obtained via {@link HttpClient#intercept()}.

- * - * Multiple interceptors can be added to this chain by using the various {@code next*()} methods available, before - * calling {@link #finish()} that returns a new {@link HttpClient} which inherits all the configuration from the parent - * client (from which this chain was created) and adds these interceptors. - * - *

Order of execution

- * - * Interceptors are executed in the order in which they are added. - * - * @param The type of the content of request. - * @param The type of the content of response. - */ -public interface HttpClientInterceptorChain { - - /** - * Adds a simple interceptor that does not change the type of objects read/written to a connection. - * - * @param interceptor Interceptor to add. - * - * @return {@code this} - */ - HttpClientInterceptorChain next(Interceptor interceptor); - - /** - * Adds an interceptor that changes the type of objects read from the connections created by the client provided by - * this chain. - * - * @param interceptor Interceptor to add. - * - * @return A new chain instance. - */ - HttpClientInterceptorChain nextWithReadTransform(TransformingInterceptor interceptor); - - /** - * Adds an interceptor that changes the type of objects written to the connections created by the client provided by - * this chain. - * - * @param interceptor Interceptor to add. - * - * @return A new chain instance. - */ - HttpClientInterceptorChain nextWithWriteTransform(TransformingInterceptor interceptor); - - /** - * Adds an interceptor that changes the type of objects read and written to the connections created by the client - * provided by this chain. - * - * @param interceptor Interceptor to add. - * - * @return A new chain instance. - */ - HttpClientInterceptorChain nextWithTransform(TransformingInterceptor interceptor); - - /** - * Finish the addition of interceptors and create a new client instance. - * - * @return New client instance which inherits all the configuration from the parent client - * (from which this chain was created) and adds these interceptors. - */ - InterceptingHttpClient finish(); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientInterceptorChainImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientInterceptorChainImpl.java deleted file mode 100644 index c5b3b61..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientInterceptorChainImpl.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -import io.reactivex.netty.protocol.http.client.events.HttpClientEventPublisher; - -final class HttpClientInterceptorChainImpl implements HttpClientInterceptorChain { - - private final RequestProvider rp; - private final HttpClientEventPublisher cep; - - HttpClientInterceptorChainImpl(RequestProvider rp, HttpClientEventPublisher cep) { - this.rp = rp; - this.cep = cep; - } - - @Override - public HttpClientInterceptorChain next(Interceptor i) { - return new HttpClientInterceptorChainImpl<>(i.intercept(rp), cep); - } - - @Override - public HttpClientInterceptorChain nextWithReadTransform(TransformingInterceptor i) { - return new HttpClientInterceptorChainImpl<>(i.intercept(rp), cep); - } - - @Override - public HttpClientInterceptorChain nextWithWriteTransform(TransformingInterceptor i) { - return new HttpClientInterceptorChainImpl<>(i.intercept(rp), cep); - } - - @Override - public HttpClientInterceptorChain nextWithTransform(TransformingInterceptor i) { - return new HttpClientInterceptorChainImpl<>(i.intercept(rp), cep); - } - - @Override - public InterceptingHttpClient finish() { - return new InterceptingHttpClientImpl<>(rp, cep); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientRequest.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientRequest.java deleted file mode 100644 index 655ed45..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientRequest.java +++ /dev/null @@ -1,575 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.cookie.Cookie; -import io.reactivex.netty.channel.AllocatingTransformer; -import io.reactivex.netty.protocol.http.TrailingHeaders; -import io.reactivex.netty.protocol.http.ws.client.WebSocketRequest; -import rx.Observable; -import rx.annotations.Experimental; -import rx.functions.Func0; -import rx.functions.Func1; -import rx.functions.Func2; - -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -/** - * An HTTP request. An instance of a request can only be created from an associated {@link HttpClient} and can be - * modified after creation. - * - *

Request URIs

- * - * While creating a request, the user should provide a URI to be used for the request. The URI can be relative or - * absolute. If the URI is relative (missing host and port information), the target host and port are inferred from the - * {@link HttpClient} that created the request. If the URI is absolute, the host and port are used from the URI. - * - *

Mutations

- * - * All mutations to this request creates a brand new instance. - - *

Trailing headers

- * - * One can write HTTP trailing headers by using - * - *

Executing request

- * - * The request is executed every time {@link HttpClientRequest}, or {@link Observable} returned by - * {@code write*Content} is subscribed and is the only way of executing the request. - * - * @param The type of objects read from the request content. - * @param The type of objects read from the response content. - */ -public abstract class HttpClientRequest extends Observable> { - - protected HttpClientRequest(OnSubscribe> onSubscribe) { - super(onSubscribe); - } - - /** - * Uses the passed {@link Observable} as the source of content for this request. - * - * @param contentSource Content source for the request. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - public abstract Observable> writeContent(Observable contentSource); - - /** - * Uses the passed {@link Observable} as the source of content for this request. Every item is written and flushed - * immediately. - * - * @param contentSource Content source for the request. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - public abstract Observable> writeContentAndFlushOnEach(Observable contentSource); - - /** - * Uses the passed {@link Observable} as the source of content for this request. - * - * @param contentSource Content source for the request. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. All pending - * writes are flushed, iff this function returns, {@code true}. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - public abstract Observable> writeContent(Observable contentSource, - Func1 flushSelector); - - /** - * Uses the passed {@link Observable} as the source of content for this request. This method provides a way to - * write trailing headers. - * - * A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed - * {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify - * the trailing headers instance. - * - * @param contentSource Content source for the request. - * @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content. - * @param trailerMutator A function to mutate the trailing header on each item emitted from the content source. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - @Experimental - public abstract Observable> writeContent(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator); - - /** - * Uses the passed {@link Observable} as the source of content for this request. This method provides a way to - * write trailing headers. - * - * A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed - * {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify - * the trailing headers instance. - * - * @param contentSource Content source for the request. - * @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content. - * @param trailerMutator A function to mutate the trailing header on each item emitted from the content source. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. All pending - * writes are flushed, iff this function returns, {@code true}. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - @Experimental - public abstract Observable> writeContent(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator, - Func1 flushSelector); - - /** - * Uses the passed {@link Observable} as the source of content for this request. - * - * @param contentSource Content source for the request. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - public abstract Observable> writeStringContent(Observable contentSource); - - /** - * Uses the passed {@link Observable} as the source of content for this request. - * - * @param contentSource Content source for the request. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. All pending - * writes are flushed, iff this function returns, {@code true}. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - public abstract Observable> writeStringContent(Observable contentSource, - Func1 flushSelector); - - /** - * Uses the passed {@link Observable} as the source of content for this request. This method provides a way to - * write trailing headers. - * - * A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed - * {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify - * the trailing headers instance. - * - * @param contentSource Content source for the request. - * @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content. - * @param trailerMutator A function to mutate the trailing header on each item emitted from the content source. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - @Experimental - public abstract Observable> writeStringContent(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator); - - /** - * Uses the passed {@link Observable} as the source of content for this request. This method provides a way to - * write trailing headers. - * - * A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed - * {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify - * the trailing headers instance. - * - * @param contentSource Content source for the request. - * @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content. - * @param trailerMutator A function to mutate the trailing header on each item emitted from the content source. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. All pending - * writes are flushed, iff this function returns, {@code true}. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - @Experimental - public abstract Observable> writeStringContent(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator, - Func1 flushSelector); - - /** - * Uses the passed {@link Observable} as the source of content for this request. - * - * @param contentSource Content source for the request. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - public abstract Observable> writeBytesContent(Observable contentSource); - - /** - * Uses the passed {@link Observable} as the source of content for this request. - * - * @param contentSource Content source for the request. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. All pending - * writes are flushed, iff this function returns, {@code true}. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - public abstract Observable> writeBytesContent(Observable contentSource, - Func1 flushSelector); - - /** - * Uses the passed {@link Observable} as the source of content for this request. This method provides a way to - * write trailing headers. - * - * A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed - * {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify - * the trailing headers instance. - * - * @param contentSource Content source for the request. - * @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content. - * @param trailerMutator A function to mutate the trailing header on each item emitted from the content source. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - @Experimental - public abstract Observable> writeBytesContent(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator); - - /** - * Uses the passed {@link Observable} as the source of content for this request. This method provides a way to - * write trailing headers. - * - * A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed - * {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify - * the trailing headers instance. - * - * @param contentSource Content source for the request. - * @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content. - * @param trailerMutator A function to mutate the trailing header on each item emitted from the content source. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. All pending - * writes are flushed, iff this function returns, {@code true}. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - @Experimental - public abstract Observable> writeBytesContent(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator, - Func1 flushSelector); - - /** - * Enables read timeout for the response of the newly created and returned request. - * - * @param timeOut Read timeout duration. - * @param timeUnit Read timeout time unit. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest readTimeOut(int timeOut, TimeUnit timeUnit); - - /** - * Enables following HTTP redirects for the newly created and returned request. - * - * @param maxRedirects Maximum number of redirects allowed. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest followRedirects(int maxRedirects); - - /** - * Enables/disables following HTTP redirects for the newly created and returned request. - * - * @param follow {@code true} for enabling redirects, {@code false} to disable. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest followRedirects(boolean follow); - - /** - * Updates the HTTP method of the request and creates a new {@link HttpClientRequest} instance. - * - * @param method New HTTP method to use. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest setMethod(HttpMethod method); - - /** - * Updates the URI of the request and creates a new {@link HttpClientRequest} instance. - * - * @param newUri New URI to use. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest setUri(String newUri); - - /** - * Adds an HTTP header with the passed {@code name} and {@code value} to this request. - * - * @param name Name of the header. - * @param value Value for the header. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest addHeader(CharSequence name, Object value); - - /** - * Adds the HTTP headers from the passed {@code headers} to this request. - * - * @param headers Map of the headers. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest addHeaders(Map> headers); - - /** - * Adds the passed {@code cookie} to this request. - * - * @param cookie Cookie to add. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest addCookie(Cookie cookie); - - /** - * Adds the passed header as a date value to this request. The date is formatted using netty's {@link - * HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the date as per the HTTP specifications into the format: - * - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name Name of the header. - * @param value Value of the header. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest addDateHeader(CharSequence name, Date value); - - /** - * Adds multiple date values for the passed header name to this request. The date values are formatted using netty's - * {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the date as per the HTTP specifications into the format: - * - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name Name of the header. - * @param values Values for the header. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest addDateHeader(CharSequence name, Iterable values); - - /** - * Adds an HTTP header with the passed {@code name} and {@code values} to this request. - * - * @param name Name of the header. - * @param values Values for the header. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest addHeaderValues(CharSequence name, Iterable values); - - /** - * Overwrites the current value, if any, of the passed header to the passed date value for this request. The date is - * formatted using netty's {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the date - * as per the HTTP specifications into - * the format: - * - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name Name of the header. - * @param value Value of the header. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest setDateHeader(CharSequence name, Date value); - - /** - * Overwrites the current value, if any, of the passed header to the passed value for this request. - * - * @param name Name of the header. - * @param value Value of the header. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest setHeader(CharSequence name, Object value); - - /** - * Overwrites the current values, if any, of the passed headers for this request. - * - * @param headers Map of the headers. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest setHeaders(Map> headers); - - /** - * Overwrites the current value, if any, of the passed header to the passed date values for this request. The date - * is formatted using netty's {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the - * date as per the HTTP specifications - * into the format: - * - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name Name of the header. - * @param values Values of the header. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest setDateHeader(CharSequence name, Iterable values); - - /** - * Overwrites the current value, if any, of the passed header to the passed values for this request. - * - * @param name Name of the header. - * @param values Values of the header. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest setHeaderValues(CharSequence name, Iterable values); - - /** - * Removes the passed header from this request. - * - * @param name Name of the header. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest removeHeader(CharSequence name); - - /** - * Sets HTTP Connection header to the appropriate value for HTTP keep-alive. This delegates to {@link - * HttpHeaders#setKeepAlive(HttpMessage, boolean)} - * - * @param keepAlive {@code true} to enable keep alive. - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest setKeepAlive(boolean keepAlive); - - /** - * Sets the HTTP transfer encoding to chunked for this request. This delegates to {@link - * HttpHeaders#setTransferEncodingChunked(HttpMessage)} - * - * @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request. - */ - public abstract HttpClientRequest setTransferEncodingChunked(); - - /** - * Creates a new {@code HttpClientRequest} instance modifying the content type using the passed {@code transformer}. - * - * @param transformer Transformer to transform the content stream. - * - * @param New type of the content. - * - * @return A new instance of {@link HttpClientRequest} with the transformed content stream. - */ - public abstract HttpClientRequest transformContent(AllocatingTransformer transformer); - - /** - * Creates a new {@code HttpClientRequest} instance modifying the content type of the response using the - * passed {@code transformer}. - * - * @param transformer Transformer to transform the content stream. - * - * @param New type of the content. - * - * @return A new instance of {@link HttpClientRequest} with the transformed response content stream. - */ - public abstract HttpClientRequest transformResponseContent(Transformer transformer); - - /** - * Creates a new {@link WebSocketRequest}, inheriting all configurations from this request, that will request an - * upgrade to websockets from the server. - * - * @return A new {@link WebSocketRequest}. - */ - public abstract WebSocketRequest requestWebSocketUpgrade(); - - /** - * Checks whether a header with the passed name exists for this request. - * - * @param name Header name. - * - * @return {@code true} if the header exists. - */ - public abstract boolean containsHeader(CharSequence name); - - /** - * Checks whether a header with the passed name and value exists for this request. - * - * @param name Header name. - * @param value Value to check. - * @param caseInsensitiveValueMatch If the value has to be matched ignoring case. - * - * @return {@code true} if the header with the passed value exists. - */ - public abstract boolean containsHeaderWithValue(CharSequence name, CharSequence value, - boolean caseInsensitiveValueMatch); - - /** - * Fetches the value of a header, if exists, for this request. - * - * @param name Name of the header. - * - * @return The value of the header, if it exists, {@code null} otherwise. If there are multiple values for this - * header, the first value is returned. - */ - public abstract String getHeader(CharSequence name); - - /** - * Fetches all values of a header, if exists, for this request. - * - * @param name Name of the header. - * - * @return All values of the header, if it exists, {@code null} otherwise. - */ - public abstract List getAllHeaders(CharSequence name); - - /** - * Returns an iterator over the header entries. Multiple values for the same header appear as separate entries in - * the returned iterator. - * - * @return An iterator over the header entries - */ - public abstract Iterator> headerIterator(); - - /** - * Returns a new {@link Set} that contains the names of all headers in this request. Note that modifying the - * returned {@link Set} will not affect the state of this response. - */ - public abstract Set getHeaderNames(); - - /** - * Returns the HTTP version of this request. - * - * @return The HTTP version of this request. - */ - public abstract HttpVersion getHttpVersion(); - - /** - * Returns the HTTP method for this request. - * - * @return The HTTP method for this request. - */ - public abstract HttpMethod getMethod(); - - /** - * Returns the URI for this request. The returned URI does not contain the scheme, host and port portion of - * the URI. - * - * @return The URI for this request. - */ - public abstract String getUri(); - -} - diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientResponse.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientResponse.java deleted file mode 100644 index 821f373..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpClientResponse.java +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -import io.netty.channel.Channel; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.cookie.Cookie; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.ContentSource; -import io.reactivex.netty.protocol.http.internal.HttpMessageFormatter; -import io.reactivex.netty.protocol.http.sse.ServerSentEvent; -import rx.Observable; -import rx.Observable.Transformer; -import rx.Subscriber; - -import java.text.ParseException; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -/** - * HTTP response for {@link HttpClient} - * - *

Thread safety

- * - * This object is not thread-safe and must not be used by multiple threads. - * - *

Mutability

- * - * Headers and trailing headers can be mutated for this response. - */ -public abstract class HttpClientResponse { - - /** - * Returns the HTTP version for this response. - * - * @return The HTTP version for this response. - */ - public abstract HttpVersion getHttpVersion(); - - /** - * Returns the HTTP status for this response. - * - * @return The HTTP status for this response. - */ - public abstract HttpResponseStatus getStatus(); - - /** - * Returns an immutable map of cookie names and cookies contained in this response. - * - * @return An immutable map of cookie names and cookies contained in this response. - */ - public abstract Map> getCookies(); - - /** - * Checks if there is a header with the passed name in this response. - * - * @param name Name of the header. - * - * @return {@code true} if there is a header with the passed name in this response. - */ - public abstract boolean containsHeader(CharSequence name); - - /** - * Checks if there is a header with the passed name and value in this response. - * - * @param name Name of the header. - * @param value Value of the header. - * @param ignoreCaseValue {@code true} then the value comparision is done ignoring case. - * - * @return {@code true} if there is a header with the passed name and value in this response. - */ - public abstract boolean containsHeader(CharSequence name, CharSequence value, boolean ignoreCaseValue); - - /** - * Returns an iterator over the header entries. Multiple values for the same header appear as separate entries in - * the returned iterator. - * - * @return An iterator over the header entries - */ - public abstract Iterator> headerIterator(); - - /** - * Returns the value of a header with the specified name. If there are more than one values for the specified name, - * the first value is returned. - * - * @param name The name of the header to search - * @return The first header value or {@code null} if there is no such header - */ - public abstract String getHeader(CharSequence name); - - /** - * Returns the value of a header with the specified name. If there are more than one values for the specified name, - * the first value is returned. - * - * @param name The name of the header to search - * @param defaultValue Default if the header does not exist. - * - * @return The first header value or {@code defaultValue} if there is no such header - */ - public abstract String getHeader(CharSequence name, String defaultValue); - - /** - * Returns the values of headers with the specified name - * - * @param name The name of the headers to search - * - * @return A {@link List} of header values which will be empty if no values are found - */ - public abstract List getAllHeaderValues(CharSequence name); - - /** - * Returns the length of the content. - * - * @return the content length - * - * @throws NumberFormatException if the message does not have the {@code "Content-Length"} header or its value is - * not a number. - */ - public abstract long getContentLength(); - - /** - * Returns the length of the content. - * - * @param defaultValue Default value if the message does not have a {@code "Content-Length"} header or its value is - * not a number - * - * @return the content length or {@code defaultValue} if this message does not have the {@code "Content-Length"} - * header or its value is not a number - */ - public abstract long getContentLength(long defaultValue); - - /** - * Returns the date header value with the specified header name. If there are more than one header value for the - * specified header name, the first value is returned. - * The value is parsed as per the - * HTTP specifications using the format: - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name The name of the header to search - * - * @return the header value - * - * @throws ParseException if there is no such header or the header value is not a formatted date - */ - public abstract long getDateHeader(CharSequence name) throws ParseException; - - /** - * Returns the date header value with the specified header name. If there are more than one header value for the - * specified header name, the first value is returned. - * The value is parsed as per the - * HTTP specifications using the format: - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name The name of the header to search - * @param defaultValue Default value if there is no header with this name. - * - * @return the header value or {@code defaultValue} if there is no header with this name. - */ - public abstract long getDateHeader(CharSequence name, long defaultValue); - - /** - * Returns the value of the {@code "Host"} header. - */ - public abstract String getHostHeader(); - - /** - * Returns the value of the {@code "Host"} header. - * - * @param defaultValue Default if the header does not exist. - * - * @return The value of the {@code "Host"} header or {@code defaultValue} if there is no such header. - */ - public abstract String getHost(String defaultValue); - - /** - * Returns the integer header value with the specified header name. If there are more than one header value for - * the specified header name, the first value is returned. - * - * @param name The name of the header to search - * - * @return the header value - * - * @throws NumberFormatException if there is no such header or the header value is not a number - */ - public abstract int getIntHeader(CharSequence name); - - /** - * Returns the integer header value with the specified header name. If there are more than one header value for - * the specified header name, the first value is returned. - * - * @param name The name of the header to search - * @param defaultValue Default if the header does not exist. - * - * @return the header value or the {@code defaultValue} if there is no such header or the header value is not a - * number - */ - public abstract int getIntHeader(CharSequence name, int defaultValue); - - /** - * Returns {@code true} if and only if this response has the content-length header set. - */ - public abstract boolean isContentLengthSet(); - - /** - * Returns {@code true} if and only if the connection can remain open and thus 'kept alive'. This methods respects - * the value of the {@code "Connection"} header first and then the return value of - * {@link HttpVersion#isKeepAliveDefault()}. - */ - public abstract boolean isKeepAlive(); - - /** - * Checks to see if the transfer encoding of this response is chunked - * - * @return True if transfer encoding is chunked, otherwise false - */ - public abstract boolean isTransferEncodingChunked(); - - /** - * Returns a new {@link Set} that contains the names of all headers in this response. Note that modifying the - * returned {@link Set} will not affect the state of this response. - */ - public abstract Set getHeaderNames(); - - /** - * Adds an HTTP header with the passed {@code name} and {@code value} to this response. - * - * @param name Name of the header. - * @param value Value for the header. - * - * @return {@code this} - */ - public abstract HttpClientResponse addHeader(CharSequence name, Object value); - - /** - * Adds the passed {@code cookie} to this response. - * - * @param cookie Cookie to add. - * - * @return {@code this} - */ - public abstract HttpClientResponse addCookie(Cookie cookie); - - /** - * Adds the passed header as a date value to this response. The date is formatted using netty's - * {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the date as per the - * HTTP specifications into the format: - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name Name of the header. - * @param value Value of the header. - * - * @return {@code this} - */ - public abstract HttpClientResponse addDateHeader(CharSequence name, Date value); - - /** - * Adds multiple date values for the passed header name to this response. The date values are formatted using netty's - * {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the date as per the - * HTTP specifications into the format: - * - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name Name of the header. - * @param values Values for the header. - * - * @return {@code this} - */ - public abstract HttpClientResponse addDateHeader(CharSequence name, Iterable values); - - /** - * Adds an HTTP header with the passed {@code name} and {@code values} to this response. - * - * @param name Name of the header. - * @param values Values for the header. - * - * @return {@code this} - */ - public abstract HttpClientResponse addHeader(CharSequence name, Iterable values); - - /** - * Overwrites the current value, if any, of the passed header to the passed date value for this response. The date is - * formatted using netty's {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the date - * as per the HTTP specifications into - * the format: - * - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name Name of the header. - * @param value Value of the header. - * - * @return {@code this} - */ - public abstract HttpClientResponse setDateHeader(CharSequence name, Date value); - - /** - * Overwrites the current value, if any, of the passed header to the passed value for this response. - * - * @param name Name of the header. - * @param value Value of the header. - * - * @return {@code this} - */ - public abstract HttpClientResponse setHeader(CharSequence name, Object value); - - /** - * Overwrites the current value, if any, of the passed header to the passed date values for this response. The date - * is formatted using netty's {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the - * date as per the HTTP specifications - * into the format: - * - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name Name of the header. - * @param values Values of the header. - * - * @return {@code this} - */ - public abstract HttpClientResponse setDateHeader(CharSequence name, Iterable values); - - /** - * Overwrites the current value, if any, of the passed header to the passed values for this response. - * - * @param name Name of the header. - * @param values Values of the header. - * - * @return {@code this} - */ - public abstract HttpClientResponse setHeader(CharSequence name, Iterable values); - - /** - * Removes the passed header from this response. - * - * @param name Name of the header. - * - * @return {@code this} - */ - public abstract HttpClientResponse removeHeader(CharSequence name); - - /** - * Returns the content as a stream of Server sent events. - * There can only be one {@link Subscriber} to the returned {@link Observable}, any subsequent subscriptions will - * get an error. - * - * @return Stream of content as {@link ServerSentEvent} messages. - */ - public abstract ContentSource getContentAsServerSentEvents(); - - /** - * Returns the content as a stream. There can only be one {@link Subscriber} to the returned {@link Observable}, any - * subsequent subscriptions will get an error. - * - * @return Stream of content. - */ - public abstract ContentSource getContent(); - - /** - * Marks the content to be discarded. This means that the content can not be read from this response from now. - * - * @return An {@link Observable}, subscription to which will discard the content. This {@code Observable} will - * error/complete when the content errors/completes and unsubscription from here will unsubscribe from the content. - */ - public abstract Observable discardContent(); - - /** - * Transforms the type of objects read from the content of this response, using the supplied {@code transformer}. - * - * @return A new instance of {@code HttpClientResponse} with transformed content. - */ - public abstract HttpClientResponse transformContent(Transformer transformer); - - /** - * Returns the underlying channel on which this response was received. - * - * @return The underlying channel on which this response was received. - */ - public abstract Channel unsafeNettyChannel(); - - /** - * Returns the underlying connection on which this response was received. - * - * @return The underlying connection on which this response was received. - */ - public abstract Connection unsafeConnection(); - - public String toString() { - return HttpMessageFormatter.formatResponse(getHttpVersion(), getStatus(), headerIterator()); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpRedirectException.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpRedirectException.java deleted file mode 100644 index 4dcec7a..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/HttpRedirectException.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -/** - * An exception signifying a failed HTTP redirects. Every exception has an encapsulated {@link Reason} retrievable via - * {@link #getReason()} - */ -public class HttpRedirectException extends RuntimeException { - - private static final long serialVersionUID = 612647744832660373L; - private final Reason reason; - - public enum Reason { - RedirectLoop, - TooManyRedirects, - InvalidRedirect - } - - public HttpRedirectException(Reason reason) { - this.reason = reason; - } - - public HttpRedirectException(Reason reason, Throwable cause) { - super(getMsgWithReason(reason), cause); - this.reason = reason; - } - - public HttpRedirectException(Reason reason, String message) { - super(getMsgWithReason(reason, message)); - this.reason = reason; - } - - public HttpRedirectException(Reason reason, String message, Throwable cause) { - super(getMsgWithReason(reason, message), cause); - this.reason = reason; - } - - public Reason getReason() { - return reason; - } - - private static String getMsgWithReason(Reason reason) { - return "Redirect failed. Reason: " + reason; - } - - private static String getMsgWithReason(Reason reason, String message) { - return getMsgWithReason(reason) + ". Error: " + message; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/InterceptingHttpClient.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/InterceptingHttpClient.java deleted file mode 100644 index 94b3308..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/InterceptingHttpClient.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpVersion; -import io.reactivex.netty.events.EventSource; -import io.reactivex.netty.protocol.http.client.events.HttpClientEventsListener; - -public abstract class InterceptingHttpClient implements EventSource { - - /** - * Creates a GET request for the passed URI. - * - * @param uri The URI for the request. The URI should be absolute and should not contain the scheme, host and port. - * - * @return New {@link HttpClientRequest}. - */ - public abstract HttpClientRequest createGet(String uri); - - /** - * Creates a POST request for the passed URI. - * - * @param uri The URI for the request. The URI should be absolute and should not contain the scheme, host and port. - * - * @return New {@link HttpClientRequest}. - */ - public abstract HttpClientRequest createPost(String uri); - - /** - * Creates a PUT request for the passed URI. - * - * @param uri The URI for the request. The URI should be absolute and should not contain the scheme, host and port. - * - * @return New {@link HttpClientRequest}. - */ - public abstract HttpClientRequest createPut(String uri); - - /** - * Creates a DELETE request for the passed URI. - * - * @param uri The URI for the request. The URI should be absolute and should not contain the scheme, host and port. - * - * @return New {@link HttpClientRequest}. - */ - public abstract HttpClientRequest createDelete(String uri); - - /** - * Creates a HEAD request for the passed URI. - * - * @param uri The URI for the request. The URI should be absolute and should not contain the scheme, host and port. - * - * @return New {@link HttpClientRequest}. - */ - public abstract HttpClientRequest createHead(String uri); - - /** - * Creates an OPTIONS request for the passed URI. - * - * @param uri The URI for the request. The URI should be absolute and should not contain the scheme, host and port. - * - * @return New {@link HttpClientRequest}. - */ - public abstract HttpClientRequest createOptions(String uri); - - /** - * Creates a PATCH request for the passed URI. - * - * @param uri The URI for the request. The URI should be absolute and should not contain the scheme, host and port. - * - * @return New {@link HttpClientRequest}. - */ - public abstract HttpClientRequest createPatch(String uri); - - /** - * Creates a TRACE request for the passed URI. - * - * @param uri The URI for the request. The URI should be absolute and should not contain the scheme, host and port. - * - * @return New {@link HttpClientRequest}. - */ - public abstract HttpClientRequest createTrace(String uri); - - /** - * Creates a CONNECT request for the passed URI. - * - * @param uri The URI for the request. The URI should be absolute and should not contain the scheme, host and port. - * - * @return New {@link HttpClientRequest}. - */ - public abstract HttpClientRequest createConnect(String uri); - - /** - * Creates a request for the passed HTTP method and URI. - * - * @param method Http Method. - * @param uri The URI for the request. The URI should be absolute and should not contain the scheme, host and port. - * - * @return New {@link HttpClientRequest}. - */ - public abstract HttpClientRequest createRequest(HttpMethod method, String uri); - - /** - * Creates a request for the passed HTTP version, method and URI. - * - * @param version HTTP version - * @param method Http Method. - * @param uri The URI for the request. The URI should be absolute and should not contain the scheme, host and port. - * - * @return New {@link HttpClientRequest}. - */ - public abstract HttpClientRequest createRequest(HttpVersion version, HttpMethod method, String uri); - - /** - * Starts the process of adding interceptors to this client. Interceptors help in achieving various usecases of - * instrumenting and transforming connections. - * - * @return A new interceptor chain to add the various interceptors. - */ - public abstract HttpClientInterceptorChain intercept(); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/InterceptingHttpClientImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/InterceptingHttpClientImpl.java deleted file mode 100644 index 5967e3f..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/InterceptingHttpClientImpl.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpVersion; -import io.reactivex.netty.protocol.http.client.events.HttpClientEventPublisher; -import io.reactivex.netty.protocol.http.client.events.HttpClientEventsListener; -import rx.Subscription; - -class InterceptingHttpClientImpl extends InterceptingHttpClient { - - private final RequestProvider requestProvider; - private final HttpClientEventPublisher cep; - - public InterceptingHttpClientImpl(RequestProvider requestProvider, HttpClientEventPublisher cep) { - this.requestProvider = requestProvider; - this.cep = cep; - } - - @Override - public HttpClientRequest createGet(String uri) { - return createRequest(HttpMethod.GET, uri); - } - - @Override - public HttpClientRequest createPost(String uri) { - return createRequest(HttpMethod.POST, uri); - } - - @Override - public HttpClientRequest createPut(String uri) { - return createRequest(HttpMethod.PUT, uri); - } - - @Override - public HttpClientRequest createDelete(String uri) { - return createRequest(HttpMethod.DELETE, uri); - } - - @Override - public HttpClientRequest createHead(String uri) { - return createRequest(HttpMethod.HEAD, uri); - } - - @Override - public HttpClientRequest createOptions(String uri) { - return createRequest(HttpMethod.OPTIONS, uri); - } - - @Override - public HttpClientRequest createPatch(String uri) { - return createRequest(HttpMethod.PATCH, uri); - } - - @Override - public HttpClientRequest createTrace(String uri) { - return createRequest(HttpMethod.TRACE, uri); - } - - @Override - public HttpClientRequest createConnect(String uri) { - return createRequest(HttpMethod.CONNECT, uri); - } - - @Override - public HttpClientRequest createRequest(HttpMethod method, String uri) { - return createRequest(HttpVersion.HTTP_1_1, method, uri); - } - - @Override - public HttpClientRequest createRequest(HttpVersion version, HttpMethod method, String uri) { - return requestProvider.createRequest(version, method, uri); - } - - @Override - public HttpClientInterceptorChain intercept() { - return new HttpClientInterceptorChainImpl<>(requestProvider, cep); - } - - @Override - public Subscription subscribe(HttpClientEventsListener listener) { - return cep.subscribe(listener); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/Interceptor.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/Interceptor.java deleted file mode 100644 index 89d0004..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/Interceptor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -/** - * An interceptor that preserves the type of client request and response content. - * - * @param The type of the content of request. - * @param The type of the content of response. - */ -public interface Interceptor { - - /** - * Intercepts and optionally changes the passed {@code RequestProvider}. - * - * @param provider Provider to intercept. - * - * @return Provider to use after this transformation. - */ - RequestProvider intercept(RequestProvider provider); - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/RequestProvider.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/RequestProvider.java deleted file mode 100644 index 89c8721..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/RequestProvider.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpVersion; - -/** - * An abstraction that creates new instance of {@link HttpClientRequest}. - * - * @param The type of the content of request. - * @param The type of the content of response. - */ -public interface RequestProvider { - - /** - * Creates a new {@link HttpClientRequest} with the provided {@code version}, {@code method} and {@code uri} - * - * @param version HTTP version. - * @param method HTTP method. - * @param uri URI. - * - * @return A new instance of {@code HttpClientRequest} - */ - HttpClientRequest createRequest(HttpVersion version, HttpMethod method, String uri); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/TransformingInterceptor.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/TransformingInterceptor.java deleted file mode 100644 index 052afef..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/TransformingInterceptor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -/** - * An interceptor that preserves the type of client request and response content. - * - * @param The type of the content of request. - * @param The type of the content of response. - */ -public interface TransformingInterceptor { - - /** - * Intercepts and changes the passed {@code RequestProvider}. - * - * @param provider Provider to intercept. - * - * @return Provider to use after this transformation. - */ - RequestProvider intercept(RequestProvider provider); - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventPublisher.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventPublisher.java deleted file mode 100644 index a5c33e9..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventPublisher.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client.events; - -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.events.EventSource; -import io.reactivex.netty.events.ListenersHolder; -import io.reactivex.netty.events.internal.SafeEventListener; -import io.reactivex.netty.protocol.tcp.client.events.TcpClientEventListener; -import io.reactivex.netty.protocol.tcp.client.events.TcpClientEventPublisher; -import rx.Subscription; -import rx.functions.Action1; -import rx.functions.Action2; -import rx.functions.Action3; -import rx.functions.Action4; -import rx.subscriptions.CompositeSubscription; - -import java.util.concurrent.TimeUnit; - -public final class HttpClientEventPublisher extends HttpClientEventsListener - implements EventSource, EventPublisher { - - private static final Action1 REQUEST_SUBMIT_ACTION = - new Action1() { - @Override - public void call(HttpClientEventsListener listener) { - listener.onRequestSubmitted(); - } - }; - - private static final Action1 REQUEST_WRITE_START_ACTION = - new Action1() { - @Override - public void call(HttpClientEventsListener listener) { - listener.onRequestWriteStart(); - } - }; - - private static final Action3 REQUEST_WRITE_COMPLETE_ACTION = - new Action3() { - @Override - public void call(HttpClientEventsListener listener, Long duration, TimeUnit timeUnit) { - listener.onRequestWriteComplete(duration, timeUnit); - } - }; - - private static final Action4 REQUEST_WRITE_FAILED_ACTION = - new Action4() { - @Override - public void call(HttpClientEventsListener listener, Long duration, TimeUnit timeUnit, Throwable t) { - listener.onRequestWriteFailed(duration, timeUnit, t); - } - }; - - private static final Action4 RESP_HEADER_RECIEVED_ACTION = - new Action4() { - @Override - public void call(HttpClientEventsListener listener, Long duration, TimeUnit timeUnit, - Integer responseCode) { - listener.onResponseHeadersReceived(responseCode, duration, timeUnit); - } - }; - - private static final Action1 RESP_CONTENT_RECIEVED_ACTION = - new Action1() { - @Override - public void call(HttpClientEventsListener listener) { - listener.onResponseContentReceived(); - } - }; - - private static final Action3 RESP_RECIEVE_COMPLETE_ACTION = - new Action3() { - @Override - public void call(HttpClientEventsListener listener, Long duration, TimeUnit timeUnit) { - listener.onResponseReceiveComplete(duration, timeUnit); - } - }; - - private static final Action2 RESP_FAILED_ACTION = - new Action2() { - @Override - public void call(HttpClientEventsListener listener, Throwable t) { - listener.onResponseFailed(t); - } - }; - - private static final Action3 PROCESSING_COMPLETE_ACTION = - new Action3() { - @Override - public void call(HttpClientEventsListener listener, Long duration, TimeUnit timeUnit) { - listener.onRequestProcessingComplete(duration, timeUnit); - } - }; - - private final ListenersHolder listeners; - private final TcpClientEventPublisher tcpDelegate; - - public HttpClientEventPublisher() { - listeners = new ListenersHolder<>(); - tcpDelegate = new TcpClientEventPublisher(); - } - - private HttpClientEventPublisher(ListenersHolder l, TcpClientEventPublisher tcpDelegate) { - listeners = new ListenersHolder<>(l); - this.tcpDelegate = tcpDelegate; - } - - @Override - public void onRequestSubmitted() { - listeners.invokeListeners(REQUEST_SUBMIT_ACTION); - } - - @Override - public void onRequestWriteStart() { - listeners.invokeListeners(REQUEST_WRITE_START_ACTION); - } - - @Override - public void onRequestWriteComplete(final long duration, final TimeUnit timeUnit) { - listeners.invokeListeners(REQUEST_WRITE_COMPLETE_ACTION, duration, timeUnit); - } - - @Override - public void onRequestWriteFailed(final long duration, final TimeUnit timeUnit, final Throwable throwable) { - listeners.invokeListeners(REQUEST_WRITE_FAILED_ACTION, duration, timeUnit, throwable); - } - - @Override - public void onResponseHeadersReceived(final int responseCode, long duration, TimeUnit timeUnit) { - listeners.invokeListeners(RESP_HEADER_RECIEVED_ACTION, duration, timeUnit, responseCode); - } - - @Override - public void onResponseContentReceived() { - listeners.invokeListeners(RESP_CONTENT_RECIEVED_ACTION); - } - - @Override - public void onResponseReceiveComplete(final long duration, final TimeUnit timeUnit) { - listeners.invokeListeners(RESP_RECIEVE_COMPLETE_ACTION, duration, timeUnit); - } - - @Override - public void onResponseFailed(final Throwable throwable) { - listeners.invokeListeners(RESP_FAILED_ACTION, throwable); - } - - @Override - public void onRequestProcessingComplete(final long duration, final TimeUnit timeUnit) { - listeners.invokeListeners(PROCESSING_COMPLETE_ACTION, duration, timeUnit); - } - - @Override - public void onConnectionCloseFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - tcpDelegate.onConnectionCloseFailed(duration, timeUnit, throwable); - } - - @Override - public void onConnectStart() { - tcpDelegate.onConnectStart(); - } - - @Override - public void onConnectSuccess(long duration, TimeUnit timeUnit) { - tcpDelegate.onConnectSuccess(duration, timeUnit); - } - - @Override - public void onConnectFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - tcpDelegate.onConnectFailed(duration, timeUnit, throwable); - } - - @Override - public void onPoolReleaseStart() { - tcpDelegate.onPoolReleaseStart(); - } - - @Override - public void onPoolReleaseSuccess(long duration, TimeUnit timeUnit) { - tcpDelegate.onPoolReleaseSuccess(duration, timeUnit); - } - - @Override - public void onPoolReleaseFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - tcpDelegate.onPoolReleaseFailed(duration, timeUnit, throwable); - } - - @Override - public void onPooledConnectionEviction() { - tcpDelegate.onPooledConnectionEviction(); - } - - @Override - public void onPooledConnectionReuse() { - tcpDelegate.onPooledConnectionReuse(); - } - - @Override - public void onPoolAcquireStart() { - tcpDelegate.onPoolAcquireStart(); - } - - @Override - public void onPoolAcquireSuccess(long duration, TimeUnit timeUnit) { - tcpDelegate.onPoolAcquireSuccess(duration, timeUnit); - } - - @Override - public void onPoolAcquireFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - tcpDelegate.onPoolAcquireFailed(duration, timeUnit, throwable); - } - - @Override - public void onByteRead(long bytesRead) { - tcpDelegate.onByteRead(bytesRead); - } - - @Override - public void onByteWritten(long bytesWritten) { - tcpDelegate.onByteWritten(bytesWritten); - } - - @Override - public void onFlushStart() { - tcpDelegate.onFlushStart(); - } - - @Override - public void onFlushComplete(long duration, TimeUnit timeUnit) { - tcpDelegate.onFlushComplete(duration, timeUnit); - } - - @Override - public void onWriteStart() { - tcpDelegate.onWriteStart(); - } - - @Override - public void onWriteSuccess(long duration, TimeUnit timeUnit) { - tcpDelegate.onWriteSuccess(duration, timeUnit); - } - - @Override - public void onWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - tcpDelegate.onWriteFailed(duration, timeUnit, throwable); - } - - @Override - public void onConnectionCloseStart() { - tcpDelegate.onConnectionCloseStart(); - } - - @Override - public void onConnectionCloseSuccess(long duration, TimeUnit timeUnit) { - tcpDelegate.onConnectionCloseSuccess(duration, timeUnit); - } - - @Override - public void onCustomEvent(Object event) { - tcpDelegate.onCustomEvent(event); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { - tcpDelegate.onCustomEvent(event, duration, timeUnit); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { - tcpDelegate.onCustomEvent(event, duration, timeUnit, throwable); - } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { - tcpDelegate.onCustomEvent(event, throwable); - } - - @Override - public boolean publishingEnabled() { - return listeners.publishingEnabled(); - } - - @Override - public Subscription subscribe(HttpClientEventsListener listener) { - if (!SafeEventListener.class.isAssignableFrom(listener.getClass())) { - listener = new SafeHttpClientEventsListener(listener); - } - - CompositeSubscription cs = new CompositeSubscription(); - cs.add(listeners.subscribe(listener)); - - TcpClientEventListener tcpListener = listener; - if (listener instanceof SafeHttpClientEventsListener) { - tcpListener = ((SafeHttpClientEventsListener) listener).unwrap(); - } - - cs.add(tcpDelegate.subscribe(tcpListener)); - return cs; - } - - public EventSource asTcpEventSource() { - return new EventSource() { - @Override - public Subscription subscribe(TcpClientEventListener listener) { - if (listener instanceof HttpClientEventsListener) { - return HttpClientEventPublisher.this.subscribe((HttpClientEventsListener) listener); - } - return tcpDelegate.subscribe(listener); - } - }; - } - - public HttpClientEventPublisher copy() { - return new HttpClientEventPublisher(listeners.copy(), tcpDelegate.copy()); - } - - /*Visible for testing*/ListenersHolder getListeners() { - return listeners; - } - - /*Visible for testing*/TcpClientEventListener getTcpDelegate() { - return tcpDelegate; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventsListener.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventsListener.java deleted file mode 100644 index a5ad0a9..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventsListener.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client.events; - -import io.reactivex.netty.protocol.http.client.HttpClient; -import io.reactivex.netty.protocol.tcp.client.events.TcpClientEventListener; - -import java.util.concurrent.TimeUnit; - -/** - * A listener for all events published by {@link HttpClient} - */ -public abstract class HttpClientEventsListener extends TcpClientEventListener { - - /** - * Event when a new request is submitted for the client. - */ - public void onRequestSubmitted() {} - - /** - * Event when the write of request started. - */ - public void onRequestWriteStart() {} - - /** - * Event when a request write is completed. - * - * @param duration Time taken from the start of write to completion. - * @param timeUnit Time unit for the duration. - */ - @SuppressWarnings("unused") - public void onRequestWriteComplete(long duration, TimeUnit timeUnit) {} - - /** - * Event when a request write failed. - * - * @param duration Time taken from the start of write to failure. - * @param timeUnit Time unit for the duration. - * @param throwable Error that caused the failure. - */ - @SuppressWarnings("unused") - public void onRequestWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) {} - - /** - * Event when the response headers are received. - * - * @param responseCode The HTTP response code. - * @param duration The time between the request write completion and response header recieve. - * @param timeUnit Timeunit for the duration. - */ - @SuppressWarnings("unused") - public void onResponseHeadersReceived(int responseCode, long duration, TimeUnit timeUnit) {} - - /** - * Event whenever an HTTP response content is received (an HTTP response can have multiple content chunks, in which - * case this event will be fired as many times for the same response). - */ - public void onResponseContentReceived() {} - - /** - * Event when the response receive is completed. - * - * @param duration Time taken between receiving the response headers and completion of response. - * @param timeUnit Time unit for the duration. - */ - @SuppressWarnings("unused") - public void onResponseReceiveComplete(long duration, TimeUnit timeUnit) {} - - /** - * Event when the response failed (either it did not arrive or not arrived completely) - * - * @param throwable Error that caused the failure. - */ - @SuppressWarnings("unused") - public void onResponseFailed(Throwable throwable) {} - - /** - * Event when the entire request processing (request header write to response failed/complete) is completed. - * - * @param duration Time taken from start of write of request to response receive completion. - * @param timeUnit Time unit for the duration. - */ - @SuppressWarnings("unused") - public void onRequestProcessingComplete(long duration, TimeUnit timeUnit) {} -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/events/SafeHttpClientEventsListener.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/events/SafeHttpClientEventsListener.java deleted file mode 100644 index 09814fa..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/events/SafeHttpClientEventsListener.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client.events; - -import io.reactivex.netty.events.internal.SafeEventListener; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -final class SafeHttpClientEventsListener extends HttpClientEventsListener implements SafeEventListener { - - private final HttpClientEventsListener delegate; - - private final AtomicBoolean completed = new AtomicBoolean(); - - public SafeHttpClientEventsListener(HttpClientEventsListener delegate) { - this.delegate = delegate; - } - - @Override - public void onCompleted() { - if (completed.compareAndSet(false, true)) { - delegate.onCompleted(); - } - } - - @Override - public void onRequestSubmitted() { - if (!completed.get()) { - delegate.onRequestSubmitted(); - } - } - - @Override - public void onRequestWriteStart() { - if (!completed.get()) { - delegate.onRequestWriteStart(); - } - } - - @Override - public void onRequestWriteComplete(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onRequestWriteComplete(duration, timeUnit); - } - } - - @Override - public void onRequestWriteFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - if (!completed.get()) { - delegate.onRequestWriteFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onResponseHeadersReceived(int responseCode, long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onResponseHeadersReceived(responseCode, duration, timeUnit); - } - } - - @Override - public void onResponseContentReceived() { - if (!completed.get()) { - delegate.onResponseContentReceived(); - } - } - - @Override - public void onResponseReceiveComplete(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onResponseReceiveComplete(duration, timeUnit); - } - } - - @Override - public void onResponseFailed(Throwable throwable) { - if (!completed.get()) { - delegate.onResponseFailed(throwable); - } - } - - @Override - public void onRequestProcessingComplete(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onRequestProcessingComplete(duration, timeUnit); - } - } - - @Override - public void onConnectStart() { - if (!completed.get()) { - delegate.onConnectStart(); - } - } - - @Override - public void onConnectSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onConnectSuccess(duration, timeUnit); - } - } - - @Override - public void onConnectFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - if (!completed.get()) { - delegate.onConnectFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onPoolReleaseStart() { - if (!completed.get()) { - delegate.onPoolReleaseStart(); - } - } - - @Override - public void onPoolReleaseSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onPoolReleaseSuccess(duration, timeUnit); - } - } - - @Override - public void onPoolReleaseFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - if (!completed.get()) { - delegate.onPoolReleaseFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onPooledConnectionEviction() { - if (!completed.get()) { - delegate.onPooledConnectionEviction(); - } - } - - @Override - public void onPooledConnectionReuse() { - if (!completed.get()) { - delegate.onPooledConnectionReuse(); - } - } - - @Override - public void onPoolAcquireStart() { - if (!completed.get()) { - delegate.onPoolAcquireStart(); - } - } - - @Override - public void onPoolAcquireSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onPoolAcquireSuccess(duration, timeUnit); - } - } - - @Override - public void onPoolAcquireFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - if (!completed.get()) { - delegate.onPoolAcquireFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onByteRead(long bytesRead) { - if (!completed.get()) { - delegate.onByteRead(bytesRead); - } - } - - @Override - public void onByteWritten(long bytesWritten) { - if (!completed.get()) { - delegate.onByteWritten(bytesWritten); - } - } - - @Override - public void onFlushStart() { - if (!completed.get()) { - delegate.onFlushStart(); - } - } - - @Override - public void onFlushComplete(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onFlushComplete(duration, timeUnit); - } - } - - @Override - public void onWriteStart() { - if (!completed.get()) { - delegate.onWriteStart(); - } - } - - @Override - public void onWriteSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onWriteSuccess(duration, timeUnit); - } - } - - @Override - public void onWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - if (!completed.get()) { - delegate.onWriteFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onConnectionCloseStart() { - if (!completed.get()) { - delegate.onConnectionCloseStart(); - } - } - - @Override - public void onConnectionCloseSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onConnectionCloseSuccess(duration, timeUnit); - } - } - - @Override - public void onConnectionCloseFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - if (!completed.get()) { - delegate.onConnectionCloseFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onCustomEvent(Object event) { - if (!completed.get()) { - delegate.onCustomEvent(event); - } - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onCustomEvent(event, duration, timeUnit); - } - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { - if (!completed.get()) { - delegate.onCustomEvent(event, duration, timeUnit, throwable); - } - } - - public HttpClientEventsListener unwrap() { - return delegate; - } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { - if (!completed.get()) { - delegate.onCustomEvent(event, throwable); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof SafeHttpClientEventsListener)) { - return false; - } - - SafeHttpClientEventsListener that = (SafeHttpClientEventsListener) o; - - return !(delegate != null? !delegate.equals(that.delegate) : that.delegate != null); - - } - - @Override - public int hashCode() { - return delegate != null? delegate.hashCode() : 0; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpChannelProvider.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpChannelProvider.java deleted file mode 100644 index 7d3d9c8..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpChannelProvider.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client.internal; - -import io.netty.channel.Channel; -import io.netty.util.AttributeKey; -import io.reactivex.netty.client.ChannelProvider; -import io.reactivex.netty.protocol.http.client.events.HttpClientEventPublisher; -import io.reactivex.netty.protocol.http.client.events.HttpClientEventsListener; -import rx.Observable; -import rx.functions.Func1; - -public class HttpChannelProvider implements ChannelProvider { - - public static final AttributeKey HTTP_CLIENT_EVENT_LISTENER = - AttributeKey.valueOf("rxnetty_http_client_event_listener"); - - private final HttpClientEventPublisher hostEventPublisher; - private final ChannelProvider delegate; - - public HttpChannelProvider(HttpClientEventPublisher hostEventPublisher, ChannelProvider delegate) { - this.hostEventPublisher = hostEventPublisher; - this.delegate = delegate; - } - - @Override - public Observable newChannel(Observable input) { - if (null != delegate) { - input = delegate.newChannel(input); - } - return input.map(new Func1() { - @Override - public Channel call(Channel channel) { - channel.attr(HTTP_CLIENT_EVENT_LISTENER).set(hostEventPublisher); - return channel; - } - }); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpChannelProviderFactory.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpChannelProviderFactory.java deleted file mode 100644 index 05ab3a9..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpChannelProviderFactory.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client.internal; - -import io.reactivex.netty.client.ChannelProvider; -import io.reactivex.netty.client.ChannelProviderFactory; -import io.reactivex.netty.client.Host; -import io.reactivex.netty.client.events.ClientEventListener; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.events.EventSource; -import io.reactivex.netty.protocol.http.client.events.HttpClientEventPublisher; - -public class HttpChannelProviderFactory implements ChannelProviderFactory { - - private final HttpClientEventPublisher clientEventPublisher; - private final ChannelProviderFactory delegate; - - public HttpChannelProviderFactory(HttpClientEventPublisher clientEventPublisher) { - this(clientEventPublisher, null); - } - - public HttpChannelProviderFactory(HttpClientEventPublisher clientEventPublisher, ChannelProviderFactory delegate) { - this.clientEventPublisher = clientEventPublisher; - this.delegate = delegate instanceof HttpChannelProviderFactory - ? ((HttpChannelProviderFactory) delegate).delegate : delegate; - } - - @Override - public ChannelProvider newProvider(Host host, EventSource eventSource, - EventPublisher publisher, ClientEventListener clientPublisher) { - final HttpClientEventPublisher hostPublisher = new HttpClientEventPublisher(); - ChannelProvider delegate = null; - if (null != this.delegate) { - delegate = this.delegate.newProvider(host, eventSource, publisher, clientPublisher); - } - hostPublisher.subscribe(clientEventPublisher); - eventSource.subscribe(hostPublisher); - return new HttpChannelProvider(hostPublisher, delegate); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpClientRequestImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpClientRequestImpl.java deleted file mode 100644 index b087550..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpClientRequestImpl.java +++ /dev/null @@ -1,542 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client.internal; - -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.cookie.Cookie; -import io.reactivex.netty.channel.AllocatingTransformer; -import io.reactivex.netty.channel.AppendTransformerEvent; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.events.Clock; -import io.reactivex.netty.events.EventAttributeKeys; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.internal.VoidToAnythingCast; -import io.reactivex.netty.protocol.http.TrailingHeaders; -import io.reactivex.netty.protocol.http.client.HttpClientRequest; -import io.reactivex.netty.protocol.http.client.HttpClientResponse; -import io.reactivex.netty.protocol.http.client.events.HttpClientEventsListener; -import io.reactivex.netty.protocol.http.internal.OperatorTrailer; -import io.reactivex.netty.protocol.http.ws.client.internal.WebSocketRequestImpl; -import io.reactivex.netty.protocol.tcp.client.TcpClient; -import rx.Observable; -import rx.Subscriber; -import rx.functions.Func0; -import rx.functions.Func1; -import rx.functions.Func2; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import static java.util.concurrent.TimeUnit.*; - -public final class HttpClientRequestImpl extends HttpClientRequest { - - public static final int NO_REDIRECTS = -1; - - private final List immutableTransformers; - private final List immutableResponseTransformers; - private final RawRequest rawRequest; - private final TcpClient> client; - private final Func1 flushOnEachSelector = new Func1() { - @Override - public Boolean call(I next) { - return true; - } - }; - - private HttpClientRequestImpl(final RawRequest rawRequest, final TcpClient> client, - List immutableTransformers, - List immutableResponseTransformers) { - super(new OnSubscribeFuncImpl<>(client, rawRequest, immutableResponseTransformers, immutableTransformers)); - this.rawRequest = rawRequest; - this.client = client; - this.immutableTransformers = immutableTransformers; - this.immutableResponseTransformers = immutableResponseTransformers; - } - - @Override - public Observable> writeContent(Observable contentSource) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource; - return _writeContentRaw(rawObservable, false); - } - - @Override - public Observable> writeContentAndFlushOnEach(Observable contentSource) { - return writeContent(contentSource, flushOnEachSelector); - } - - @Override - public Observable> writeStringContent(Observable contentSource) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource; - return _writeContentRaw(rawObservable, false); - } - - @Override - public Observable> writeBytesContent(Observable contentSource) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource; - return _writeContentRaw(rawObservable, false); - } - - @Override - public Observable> writeContent(Observable contentSource, - Func1 flushSelector) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource; - return _writeContentRaw(rawObservable, flushSelector, false); - } - - @Override - public Observable> writeStringContent(Observable contentSource, - Func1 flushSelector) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource; - return _writeContentRaw(rawObservable, flushSelector, false); - } - - @Override - public Observable> writeBytesContent(Observable contentSource, - Func1 flushSelector) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource; - return _writeContentRaw(rawObservable, flushSelector, false); - } - - @Override - public Observable> writeContent(Observable contentSource, - final Func0 trailerFactory, - final Func2 trailerMutator) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource; - return _writeContentRaw(OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), true); - } - - @Override - public Observable> writeStringContent(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource; - return _writeContentRaw(OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), true); - } - - @Override - public Observable> writeBytesContent(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource; - return _writeContentRaw(OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), true); - } - - @Override - public Observable> writeContent(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator, - Func1 flushSelector) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource; - return _writeContentRaw(OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), flushSelector, - true); - } - - @Override - public Observable> writeStringContent( - Observable contentSource, Func0 trailerFactory, Func2 trailerMutator, - Func1 flushSelector) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource; - return _writeContentRaw(OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), flushSelector, - true); - } - - @Override - public Observable> writeBytesContent( - Observable contentSource, Func0 trailerFactory, Func2 trailerMutator, - Func1 flushSelector) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource; - return _writeContentRaw(OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), flushSelector, - true); - } - - @Override - public HttpClientRequestImpl readTimeOut(int timeOut, TimeUnit timeUnit) { - return _copy(client.readTimeOut(timeOut, timeUnit)); - } - - @Override - public HttpClientRequestImpl followRedirects(int maxRedirects) { - final Redirector redirector = new Redirector<>(maxRedirects, client); - HttpClientRequestImpl toReturn = _copy(client, rawRequest.followRedirect(redirector)); - redirector.setOriginalRequest(toReturn.rawRequest); - return toReturn; - } - - @Override - public HttpClientRequestImpl followRedirects(boolean follow) { - return follow ? followRedirects(Redirector.DEFAULT_MAX_REDIRECTS) : followRedirects(NO_REDIRECTS); - } - - @Override - public HttpClientRequestImpl setMethod(HttpMethod method) { - return _copy(client, rawRequest.setMethod(method)); - } - - @Override - public HttpClientRequestImpl setUri(String newUri) { - return _copy(client, rawRequest.setUri(newUri)); - } - - @Override - public HttpClientRequestImpl addHeader(CharSequence name, Object value) { - return _copy(client, rawRequest.addHeader(name, value)); - } - - @Override - public HttpClientRequest addHeaders(Map> headers) { - return _copy(client, rawRequest.addHeaders(headers)); - } - - @Override - public HttpClientRequestImpl addCookie(Cookie cookie) { - return _copy(client, rawRequest.addCookie(cookie)); - } - - @Override - public HttpClientRequestImpl addDateHeader(CharSequence name, Date value) { - return _copy(client, rawRequest.addDateHeader(name, value)); - } - - @Override - public HttpClientRequestImpl addDateHeader(CharSequence name, Iterable values) { - return _copy(client, rawRequest.addDateHeader(name, values)); - } - - @Override - public HttpClientRequestImpl addHeaderValues(CharSequence name, Iterable values) { - return _copy(client, rawRequest.addHeaderValues(name, values)); - } - - @Override - public HttpClientRequestImpl setDateHeader(CharSequence name, Date value) { - return _copy(client, rawRequest.setDateHeader(name, value)); - } - - @Override - public HttpClientRequestImpl setHeader(CharSequence name, Object value) { - return _copy(client, rawRequest.setHeader(name, value)); - } - - @Override - public HttpClientRequest setHeaders(Map> headers) { - return _copy(client, rawRequest.setHeaders(headers)); - } - - @Override - public HttpClientRequestImpl setDateHeader(CharSequence name, Iterable values) { - return _copy(client, rawRequest.setDateHeader(name, values)); - } - - @Override - public HttpClientRequestImpl setHeaderValues(CharSequence name, Iterable values) { - return _copy(client, rawRequest.setHeaderValues(name, values)); - } - - @Override - public HttpClientRequestImpl removeHeader(CharSequence name) { - return _copy(client, rawRequest.removeHeader(name)); - } - - @Override - public HttpClientRequestImpl setKeepAlive(boolean keepAlive) { - return _copy(client, rawRequest.setKeepAlive(keepAlive)); - } - - @Override - public HttpClientRequestImpl setTransferEncodingChunked() { - return _copy(client, rawRequest.setTransferEncodingChunked()); - } - - @Override - public HttpClientRequestImpl transformContent(AllocatingTransformer transformer) { - final List newTransformers = new ArrayList<>(immutableTransformers); - @SuppressWarnings("unchecked") - AppendTransformerEvent e = new AppendTransformerEvent(transformer); - newTransformers.add(e); - @SuppressWarnings("unchecked") - RawRequest cast = (RawRequest) this.rawRequest; - return new HttpClientRequestImpl<>(cast, client, newTransformers, immutableResponseTransformers); - } - - @Override - public HttpClientRequestImpl transformResponseContent(Transformer transformer) { - final List newTransformers = new ArrayList<>(immutableResponseTransformers); - newTransformers.add(transformer); - @SuppressWarnings("unchecked") - RawRequest cast = (RawRequest) this.rawRequest; - TcpClient rawClient = client; - @SuppressWarnings("unchecked") - TcpClient> _client = (TcpClient>)rawClient; - return new HttpClientRequestImpl<>(cast, _client, immutableTransformers, newTransformers); - } - - @Override - public WebSocketRequestImpl requestWebSocketUpgrade() { - return WebSocketRequestImpl.createNew(this); - } - - @Override - public boolean containsHeader(CharSequence name) { - return rawRequest.getHeaders().headers().contains(name); - } - - @Override - public boolean containsHeaderWithValue(CharSequence name, CharSequence value, boolean caseInsensitiveValueMatch) { - return rawRequest.getHeaders().headers().contains(name, value, caseInsensitiveValueMatch); - } - - @Override - public String getHeader(CharSequence name) { - return rawRequest.getHeaders().headers().get(name); - } - - @Override - public List getAllHeaders(CharSequence name) { - return rawRequest.getHeaders().headers().getAll(name); - } - - @Override - public Iterator> headerIterator() { - return rawRequest.getHeaders().headers().iteratorCharSequence(); - } - - @Override - public Set getHeaderNames() { - return rawRequest.getHeaders().headers().names(); - } - - @Override - public HttpVersion getHttpVersion() { - return rawRequest.getHeaders().protocolVersion(); - } - - @Override - public HttpMethod getMethod() { - return rawRequest.getHeaders().method(); - } - - @Override - public String getUri() { - return rawRequest.getHeaders().uri(); - } - - public static HttpClientRequestImpl create(final HttpVersion version, final HttpMethod httpMethod, - final String uri, - final TcpClient> client, - int maxRedirects) { - Redirector redirector = NO_REDIRECTS == maxRedirects - ? null - : new Redirector(maxRedirects, client - ); - - final RawRequest rawRequest = RawRequest.create(version, httpMethod, uri, redirector); - - if (null != redirector) { - redirector.setOriginalRequest(rawRequest); - } - - return create(rawRequest, client); - } - - public static HttpClientRequestImpl create(final HttpVersion version, final HttpMethod httpMethod, - final String uri, - final TcpClient> client) { - return create(version, httpMethod, uri, client, NO_REDIRECTS); - } - - public static HttpClientRequestImpl create(final RawRequest rawRequest, - final TcpClient> client) { - return new HttpClientRequestImpl<>(rawRequest, client, Collections.emptyList(), - Collections.emptyList()); - } - - public TcpClient> getClient() { - return client; - } - - @SuppressWarnings("unchecked") - private HttpClientRequestImpl _copy(TcpClient> c) { - return _copy(c, (RawRequest)rawRequest); - } - - @SuppressWarnings("unchecked") - private HttpClientRequestImpl _copy(TcpClient> c, - RawRequest rawRequest) { - return new HttpClientRequestImpl<>(rawRequest, c, immutableTransformers, immutableResponseTransformers); - } - - @SuppressWarnings("rawtypes") - private Observable> _writeContentRaw(Observable rawContent, boolean hasTrailers) { - return _writeContentRaw(rawContent, null, hasTrailers); - } - - @SuppressWarnings("rawtypes") - private Observable> _writeContentRaw(Observable rawContent, - Func1 flushSelector, boolean hasTrailers) { - final RawRequest r = RawRequest.create(rawRequest.getHeaders(), rawContent, flushSelector, hasTrailers, - rawRequest.getRedirector()); - return new HttpClientRequestImpl<>(r, client, immutableTransformers, immutableResponseTransformers); - } - - public RawRequest unsafeRawRequest() { - return rawRequest; - } - - private static class OnSubscribeFuncImpl implements OnSubscribe> { - @SuppressWarnings("rawtypes") - private final Observable source; - private final TcpClient> client; - - public OnSubscribeFuncImpl(final TcpClient> client, RawRequest rawRequest, - List responseTransformers, - List requestTransformers) { - this.client = client; - ConnToResponseFunc connToResponseFunc = new ConnToResponseFunc<>(rawRequest, responseTransformers, - requestTransformers); - Observable> source = this.client.createConnectionRequest() - .take(1) - .switchMap(connToResponseFunc); - - if (null != rawRequest.getRedirector()) { - source = source.switchMap(rawRequest.getRedirector()); - } - - this.source = source; - } - - @Override - @SuppressWarnings("unchecked") - public void call(Subscriber> subscriber) { - @SuppressWarnings("rawtypes") - final Subscriber rawSub = subscriber; - source.unsafeSubscribe(rawSub); - } - - } - - private static class ConnToResponseFunc - implements Func1, ?>, Observable>> { - - private final RawRequest rawRequest; - private List responseTransformers; - private List requestTransformers; - - public ConnToResponseFunc(RawRequest rawRequest, List responseTransformers, - List requestTransformers) { - this.rawRequest = rawRequest; - this.responseTransformers = responseTransformers; - this.requestTransformers = requestTransformers; - } - - @Override - public Observable> call(final Connection, ?> conn) { - for (AppendTransformerEvent requestTransformer : requestTransformers) { - conn.unsafeNettyChannel().pipeline().fireUserEventTriggered(requestTransformer); - } - - final Observable> input = conn.getInput(); - - final HttpClientEventsListener eventsListener = - conn.unsafeNettyChannel().attr(HttpChannelProvider.HTTP_CLIENT_EVENT_LISTENER).get(); - final EventPublisher eventPublisher = - conn.unsafeNettyChannel().attr(EventAttributeKeys.EVENT_PUBLISHER).get(); - - return writeRequest(conn).lift(new RequestWriteMetricsOperator(eventsListener, eventPublisher)) - .map(new VoidToAnythingCast>()) - .ignoreElements() - .concatWith(input.take(1)) - .map(new Func1, HttpClientResponse>() { - @SuppressWarnings("unchecked") - @Override - public HttpClientResponse call(HttpClientResponse r) { - HttpClientResponse rp = HttpClientResponseImpl.newInstance(r, conn); - for (Transformer transformer : responseTransformers) { - rp = rp.transformContent(transformer); - } - return (HttpClientResponse) rp; - } - }); - } - - @SuppressWarnings("unchecked") - protected Observable writeRequest(Connection, ?> conn) { - return conn.write(rawRequest.asObservable(conn)); - } - } - - private static class RequestWriteMetricsOperator implements Operator { - - private final EventPublisher eventPublisher; - private final HttpClientEventsListener eventsListener; - - public RequestWriteMetricsOperator(HttpClientEventsListener eventsListener, EventPublisher eventPublisher) { - this.eventPublisher = eventPublisher; - this.eventsListener = eventsListener; - } - - @Override - public Subscriber call(final Subscriber o) { - final long startTimeNanos = eventPublisher.publishingEnabled() ? Clock.newStartTimeNanos() : -1; - if (eventPublisher.publishingEnabled()) { - eventsListener.onRequestSubmitted(); - } - return new Subscriber(o) { - @Override - public void onCompleted() { - if (eventPublisher.publishingEnabled()) { - eventsListener.onRequestWriteComplete(Clock.onEndNanos(startTimeNanos), NANOSECONDS); - } - o.onCompleted(); - } - - @Override - public void onError(Throwable e) { - if (eventPublisher.publishingEnabled()) { - eventsListener.onRequestWriteFailed(Clock.onEndNanos(startTimeNanos), NANOSECONDS, e); - } - o.onError(e); - } - - @Override - public void onNext(Void aVoid) { - o.onNext(aVoid); - } - }; - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpClientResponseImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpClientResponseImpl.java deleted file mode 100644 index eca0992..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpClientResponseImpl.java +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client.internal; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.cookie.ClientCookieEncoder; -import io.netty.handler.codec.http.cookie.Cookie; -import io.netty.util.ReferenceCountUtil; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.ContentSource; -import io.reactivex.netty.protocol.http.CookiesHolder; -import io.reactivex.netty.protocol.http.HttpHandlerNames; -import io.reactivex.netty.protocol.http.client.HttpClientResponse; -import io.reactivex.netty.protocol.http.internal.HttpContentSubscriberEvent; -import io.reactivex.netty.protocol.http.sse.ServerSentEvent; -import io.reactivex.netty.protocol.http.sse.client.ServerSentEventDecoder; -import rx.Observable; -import rx.Observable.Transformer; -import rx.Subscriber; -import rx.functions.Func1; - -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Pattern; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; - -public final class HttpClientResponseImpl extends HttpClientResponse { - - private static final Logger logger = Logger.getLogger(HttpClientResponseImpl.class.getName()); - - public static final String KEEP_ALIVE_HEADER_NAME = "Keep-Alive"; - private static final Pattern PATTERN_COMMA = Pattern.compile(","); - private static final Pattern PATTERN_EQUALS = Pattern.compile("="); - public static final String KEEP_ALIVE_TIMEOUT_HEADER_ATTR = "timeout"; - - private final HttpResponse nettyResponse; - private final Connection connection; - private final CookiesHolder cookiesHolder; - private final ContentSource contentSource; - - private HttpClientResponseImpl(HttpResponse nettyResponse) { - this(nettyResponse, UnusableConnection.create()); - } - - private HttpClientResponseImpl(HttpResponse nettyResponse, Connection connection) { - this.nettyResponse = nettyResponse; - this.connection = connection; - cookiesHolder = CookiesHolder.newClientResponseHolder(nettyResponse.headers()); - contentSource = new ContentSource<>(unsafeNettyChannel(), new ContentSourceSubscriptionFactory()); - } - - private HttpClientResponseImpl(HttpClientResponseImpl toCopy, ContentSource newSource) { - nettyResponse = toCopy.nettyResponse; - connection = toCopy.connection; - cookiesHolder = toCopy.cookiesHolder; - contentSource = newSource; - } - - @Override - public HttpVersion getHttpVersion() { - return nettyResponse.protocolVersion(); - } - - @Override - public HttpResponseStatus getStatus() { - return nettyResponse.status(); - } - - @Override - public Map> getCookies() { - return cookiesHolder.getAllCookies(); - } - - @Override - public boolean containsHeader(CharSequence name) { - return nettyResponse.headers().contains(name); - } - - @Override - public boolean containsHeader(CharSequence name, CharSequence value, boolean ignoreCaseValue) { - return nettyResponse.headers().contains(name, value, ignoreCaseValue); - } - - @Override - public Iterator> headerIterator() { - return nettyResponse.headers().iteratorCharSequence(); - } - - @Override - public String getHeader(CharSequence name) { - return nettyResponse.headers().get(name); - } - - @Override - public String getHeader(CharSequence name, String defaultValue) { - return nettyResponse.headers().get(name, defaultValue); - } - - @Override - public List getAllHeaderValues(CharSequence name) { - return nettyResponse.headers().getAll(name); - } - - @Override - public long getContentLength() { - return HttpUtil.getContentLength(nettyResponse); - } - - @Override - public long getContentLength(long defaultValue) { - return HttpUtil.getContentLength(nettyResponse, defaultValue); - } - - @Override - public long getDateHeader(CharSequence name) { - return nettyResponse.headers().getTimeMillis(name); - } - - @Override - public long getDateHeader(CharSequence name, long defaultValue) { - return nettyResponse.headers().getTimeMillis(name, defaultValue); - } - - @Override - public String getHostHeader() { - return nettyResponse.headers().get(HOST); - } - - @Override - public String getHost(String defaultValue) { - return nettyResponse.headers().get(HOST, defaultValue); - } - - @Override - public int getIntHeader(CharSequence name) { - return nettyResponse.headers().getInt(name); - } - - @Override - public int getIntHeader(CharSequence name, int defaultValue) { - return nettyResponse.headers().getInt(name, defaultValue); - } - - @Override - public boolean isContentLengthSet() { - return HttpUtil.isContentLengthSet(nettyResponse); - } - - @Override - public boolean isKeepAlive() { - return HttpUtil.isKeepAlive(nettyResponse); - } - - @Override - public boolean isTransferEncodingChunked() { - return HttpUtil.isTransferEncodingChunked(nettyResponse); - } - - @Override - public Set getHeaderNames() { - return nettyResponse.headers().names(); - } - - @Override - public HttpClientResponse addHeader(CharSequence name, Object value) { - nettyResponse.headers().add(name, value); - return this; - } - - @Override - public HttpClientResponse addCookie(Cookie cookie) { - nettyResponse.headers().add(SET_COOKIE, ClientCookieEncoder.STRICT.encode(cookie)); - return this; - } - - @Override - public HttpClientResponse addDateHeader(CharSequence name, Date value) { - nettyResponse.headers().set(name, value); - return this; - } - - @Override - public HttpClientResponse addDateHeader(CharSequence name, Iterable values) { - for (Date value : values) { - nettyResponse.headers().add(name, value); - } - return this; - } - - @Override - public HttpClientResponse addHeader(CharSequence name, Iterable values) { - nettyResponse.headers().add(name, values); - return this; - } - - @Override - public HttpClientResponse setDateHeader(CharSequence name, Date value) { - nettyResponse.headers().set(name, value); - return this; - } - - @Override - public HttpClientResponse setHeader(CharSequence name, Object value) { - nettyResponse.headers().set(name, value); - return this; - } - - @Override - public HttpClientResponse setDateHeader(CharSequence name, Iterable values) { - for (Date value : values) { - nettyResponse.headers().set(name, value); - } - return this; - } - - @Override - public HttpClientResponse setHeader(CharSequence name, Iterable values) { - nettyResponse.headers().set(name, values); - return this; - } - - @Override - public HttpClientResponse removeHeader(CharSequence name) { - nettyResponse.headers().remove(name); - return this; - } - - @Override - public ContentSource getContentAsServerSentEvents() { - if (containsHeader(CONTENT_TYPE) && getHeader(CONTENT_TYPE).startsWith("text/event-stream")) { - ChannelPipeline pipeline = unsafeNettyChannel().pipeline(); - ChannelHandlerContext decoderCtx = pipeline.context(HttpHandlerNames.HttpClientCodec.getName()); - if (null != decoderCtx) { - pipeline.addAfter(decoderCtx.name(), HttpHandlerNames.SseClientCodec.getName(), - new ServerSentEventDecoder()); - } - return new ContentSource<>(unsafeNettyChannel(), new ContentSourceSubscriptionFactory()); - } - - return new ContentSource<>(new IllegalStateException("Response is not a server sent event response.")); - } - - @Override - public ContentSource getContent() { - return contentSource; - } - - @Override - public Observable discardContent() { - return getContent().map(new Func1() { - @Override - public Void call(T t) { - ReferenceCountUtil.release(t); - return null; - } - }).ignoreElements(); - } - - @Override - public HttpClientResponse transformContent(Transformer transformer) { - return new HttpClientResponseImpl<>(this, contentSource.transform(transformer)); - } - - @Override - public Channel unsafeNettyChannel() { - return unsafeConnection().unsafeNettyChannel(); - } - - @Override - public Connection unsafeConnection() { - return connection; - } - - /** - * Parses the timeout value from the HTTP keep alive header (with name {@link #KEEP_ALIVE_HEADER_NAME}) as described in - * this spec - * - * @return The keep alive timeout or {@code null} if this response does not define the appropriate header value. - */ - public Long getKeepAliveTimeoutSeconds() { - String keepAliveHeader = nettyResponse.headers().get(KEEP_ALIVE_HEADER_NAME); - if (null != keepAliveHeader && !keepAliveHeader.isEmpty()) { - String[] pairs = PATTERN_COMMA.split(keepAliveHeader); - if (pairs != null) { - for (String pair: pairs) { - String[] nameValue = PATTERN_EQUALS.split(pair.trim()); - if (nameValue != null && nameValue.length >= 2) { - String name = nameValue[0].trim().toLowerCase(); - String value = nameValue[1].trim(); - if (KEEP_ALIVE_TIMEOUT_HEADER_ATTR.equals(name)) { - try { - return Long.valueOf(value); - } catch (NumberFormatException e) { - logger.log(Level.INFO, "Invalid HTTP keep alive timeout value. Keep alive header: " - + keepAliveHeader + ", timeout attribute value: " + nameValue[1], e); - return null; - } - } - } - } - } - } - return null; - } - - /*Visible for the client bridge*/static HttpClientResponseImpl unsafeCreate(HttpResponse nettyResponse) { - return new HttpClientResponseImpl<>(nettyResponse); - } - - public static HttpClientResponse newInstance(HttpClientResponse unsafeInstance, - Connection connection) { - HttpClientResponseImpl cast = (HttpClientResponseImpl) unsafeInstance; - return new HttpClientResponseImpl<>(cast.nettyResponse, connection); - } - - public static HttpClientResponse newInstance(HttpResponse nettyResponse, Connection connection) { - return new HttpClientResponseImpl<>(nettyResponse, connection); - } - - private static class ContentSourceSubscriptionFactory implements Func1, Object> { - @Override - public Object call(Subscriber subscriber) { - return new HttpContentSubscriberEvent<>(subscriber); - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpClientToConnectionBridge.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpClientToConnectionBridge.java deleted file mode 100644 index ecc72cb..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/HttpClientToConnectionBridge.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client.internal; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.util.AttributeKey; -import io.reactivex.netty.client.ClientConnectionToChannelBridge; -import io.reactivex.netty.client.ClientConnectionToChannelBridge.ConnectionReuseEvent; -import io.reactivex.netty.client.ClientConnectionToChannelBridge.PooledConnectionReleaseEvent; -import io.reactivex.netty.client.pool.PooledConnection; -import io.reactivex.netty.events.Clock; -import io.reactivex.netty.events.EventAttributeKeys; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.protocol.http.client.events.HttpClientEventsListener; -import io.reactivex.netty.protocol.http.internal.AbstractHttpConnectionBridge; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; - -import static java.util.concurrent.TimeUnit.*; - -public class HttpClientToConnectionBridge extends AbstractHttpConnectionBridge { - - /** - * This attribute stores the value of any dynamic idle timeout value sent via an HTTP keep alive header. - * This follows the proposal specified here: http://tools.ietf.org/id/draft-thomson-hybi-http-timeout-01.html - * The attribute can be extracted from an HTTP response header using the helper method - * {@link HttpClientResponseImpl#getKeepAliveTimeoutSeconds()} - */ - public static final AttributeKey KEEP_ALIVE_TIMEOUT_MILLIS_ATTR = - PooledConnection.DYNAMIC_CONN_KEEP_ALIVE_TIMEOUT_MS; - - private HttpClientEventsListener eventsListener; - private EventPublisher eventPublisher; - private String hostHeader; - private long requestWriteCompletionTimeNanos; - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - eventsListener = ctx.channel().attr(HttpChannelProvider.HTTP_CLIENT_EVENT_LISTENER).get(); - eventPublisher = ctx.channel().attr(EventAttributeKeys.EVENT_PUBLISHER).get(); - super.handlerAdded(ctx); - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - SocketAddress remoteAddr = ctx.channel().remoteAddress(); - if (remoteAddr instanceof InetSocketAddress) { - InetSocketAddress inetSock = (InetSocketAddress) remoteAddr; - String hostString = inetSock.getHostString(); // Don't use hostname that does a DNS lookup. - hostHeader = hostString + ':' + inetSock.getPort(); - } - super.channelActive(ctx); - } - - @Override - protected void beforeOutboundHeaderWrite(HttpMessage httpMsg, ChannelPromise promise, long startTimeNanos) { - /*Reset on every request write, we do not currently support pipelining, otherwise, this should be stored in a - queue.*/ - requestWriteCompletionTimeNanos = -1; - if (null != hostHeader) { - if (!httpMsg.headers().contains(HttpHeaderNames.HOST)) { - httpMsg.headers().set(HttpHeaderNames.HOST, hostHeader); - } - } - if (eventPublisher.publishingEnabled()) { - eventsListener.onRequestWriteStart(); - } - } - - @Override - protected void onOutboundLastContentWrite(LastHttpContent msg, ChannelPromise promise, - final long headerWriteStartTimeNanos) { - if (eventPublisher.publishingEnabled()) { - promise.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (eventPublisher.publishingEnabled()) { - requestWriteCompletionTimeNanos = Clock.newStartTimeNanos(); - if (future.isSuccess()) { - eventsListener.onRequestWriteComplete(Clock.onEndNanos(headerWriteStartTimeNanos), - NANOSECONDS); - } else { - eventsListener.onRequestWriteFailed(Clock.onEndNanos(headerWriteStartTimeNanos), - NANOSECONDS, future.cause()); - } - } - } - }); - } - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof ConnectionReuseEvent) { - resetSubscriptionState(connectionInputSubscriber); - connectionInputSubscriber = null; - } else if (PooledConnectionReleaseEvent.INSTANCE == evt) { - onPooledConnectionRelease(connectionInputSubscriber); - } - super.userEventTriggered(ctx, evt); - } - - @Override - protected void onClosedBeforeReceiveComplete(Channel channel) { - if (channel.isActive()) { - /* - * If the close is triggerred by the user, the channel will be active. - * If the response, isn't complete, then the connection can not be used. - */ - channel.attr(ClientConnectionToChannelBridge.DISCARD_CONNECTION).set(true); - } - } - - @Override - protected boolean isInboundHeader(Object nextItem) { - return nextItem instanceof HttpResponse; - } - - @Override - protected boolean isOutboundHeader(Object nextItem) { - return nextItem instanceof HttpRequest; - } - - @Override - protected Object newHttpObject(Object nextItem, Channel channel) { - final HttpResponse nettyResponse = (HttpResponse) nextItem; - - if (eventPublisher.publishingEnabled()) { - long duration = -1; - if (requestWriteCompletionTimeNanos != -1) { - duration = Clock.onEndNanos(requestWriteCompletionTimeNanos); - } - eventsListener.onResponseHeadersReceived(nettyResponse.status().code(), duration, NANOSECONDS); - } - - final HttpClientResponseImpl rxResponse = HttpClientResponseImpl.unsafeCreate(nettyResponse); - Long keepAliveTimeoutSeconds = rxResponse.getKeepAliveTimeoutSeconds(); - if (null != keepAliveTimeoutSeconds) { - channel.attr(KEEP_ALIVE_TIMEOUT_MILLIS_ATTR).set(keepAliveTimeoutSeconds * 1000); - } - - if (!rxResponse.isKeepAlive()) { - channel.attr(ClientConnectionToChannelBridge.DISCARD_CONNECTION).set(true); /*Discard connection when done with this response.*/ - } - - return rxResponse; - } - - @Override - protected void onContentReceived() { - if (eventPublisher.publishingEnabled()) { - eventsListener.onResponseContentReceived(); - } - } - - @Override - protected void onContentReceiveComplete(long receiveStartTimeNanos) { - connectionInputSubscriber.onCompleted(); /*Unsubscribe from the input and hence close/release connection*/ - if (eventPublisher.publishingEnabled()) { - long headerWriteStart = getHeaderWriteStartTimeNanos(); - eventsListener.onResponseReceiveComplete(Clock.onEndNanos(receiveStartTimeNanos), NANOSECONDS); - eventsListener.onRequestProcessingComplete(Clock.onEndNanos(headerWriteStart), NANOSECONDS); - } - } - - private void onPooledConnectionRelease(ConnectionInputSubscriber connectionInputSubscriber) { - onChannelClose(connectionInputSubscriber); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/RawRequest.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/RawRequest.java deleted file mode 100644 index 1cf1ab3..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/RawRequest.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client.internal; - -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.codec.http.cookie.ClientCookieEncoder; -import io.netty.handler.codec.http.cookie.Cookie; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.FlushSelectorOperator; -import rx.Observable; -import rx.functions.Func1; - -import java.util.Date; -import java.util.Map; -import java.util.Map.Entry; - -public final class RawRequest { - - private final Redirector redirector; - private final HttpRequest headers; - @SuppressWarnings("rawtypes") - private final Observable content; - private final Func1 flushSelector; - private final boolean hasTrailers; - - @SuppressWarnings("rawtypes") - private RawRequest(HttpRequest headers, Observable content, Func1 flushSelector, boolean hasTrailers, - Redirector redirector) { - this.headers = headers; - this.content = content; - this.flushSelector = flushSelector; - this.hasTrailers = hasTrailers; - this.redirector = redirector; - } - - public RawRequest addHeader(CharSequence name, Object value) { - HttpRequest headersCopy = _copyHeaders(); - headersCopy.headers().add(name, value); - return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); - } - - public RawRequest addHeaders(Map> headers) { - HttpRequest headersCopy = _copyHeaders(); - for (Entry> header : headers.entrySet()) { - headersCopy.headers().add(header.getKey(), header.getValue()); - } - return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); - } - - public RawRequest addHeaderValues(CharSequence name, Iterable values) { - HttpRequest headersCopy = _copyHeaders(); - headersCopy.headers().add(name, values); - return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); - } - - public RawRequest addCookie(Cookie cookie) { - String cookieHeader = ClientCookieEncoder.STRICT.encode(cookie); - return addHeader(HttpHeaderNames.COOKIE, cookieHeader); - } - - public RawRequest addDateHeader(CharSequence name, Date value) { - HttpRequest headersCopy = _copyHeaders(); - headersCopy.headers().add(name, value); - return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); - } - - public RawRequest addDateHeader(CharSequence name, Iterable values) { - HttpRequest headersCopy = _copyHeaders(); - for (Date value : values) { - headersCopy.headers().add(name, value); - } - return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); - } - - public RawRequest setDateHeader(CharSequence name, Date value) { - HttpRequest headersCopy = _copyHeaders(); - headersCopy.headers().set(name, value); - return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); - } - - public RawRequest setHeader(CharSequence name, Object value) { - HttpRequest headersCopy = _copyHeaders(); - headersCopy.headers().set(name, value); - return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); - } - - public RawRequest setHeaders(Map> headers) { - HttpRequest headersCopy = _copyHeaders(); - for (Entry> header : headers.entrySet()) { - headersCopy.headers().set(header.getKey(), header.getValue()); - } - return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); - } - - public RawRequest setHeaderValues(CharSequence name, Iterable values) { - HttpRequest headersCopy = _copyHeaders(); - headersCopy.headers().set(name, values); - return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); - } - - public RawRequest setDateHeader(CharSequence name, Iterable values) { - HttpRequest headersCopy = _copyHeaders(); - boolean addNow = false; - for (Date value : values) { - if (addNow) { - headersCopy.headers().add(name, value); - } else { - headersCopy.headers().set(name, value); - addNow = true; - } - } - return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); - } - - public RawRequest setKeepAlive(boolean keepAlive) { - HttpRequest headersCopy = _copyHeaders(); - HttpUtil.setKeepAlive(headersCopy, keepAlive); - return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); - } - - public RawRequest setTransferEncodingChunked() { - HttpRequest headersCopy = _copyHeaders(); - HttpUtil.setTransferEncodingChunked(headersCopy, true); - return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); - } - - public RawRequest removeHeader(CharSequence name) { - HttpRequest headersCopy = _copyHeaders(); - headersCopy.headers().remove(name); - return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); - } - - public RawRequest setMethod(HttpMethod method) { - HttpRequest headersCopy = _copyHeaders(headers.uri(), method); - return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); - } - - public RawRequest setUri(String uri) { - HttpRequest headersCopy = _copyHeaders(uri, headers.method()); - return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); - } - - public RawRequest followRedirect(Redirector redirectHandler) { - return new RawRequest<>(headers, content, flushSelector, hasTrailers, redirectHandler); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - public Observable asObservable(Connection connection) { - HttpRequest headers = this.headers; - if (null == content) { - headers = _copyHeaders(); - headers.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); - } - - Observable toReturn = Observable.just(headers); - - if (null != content) { - if (null == flushSelector) { - toReturn = toReturn.concatWith(content); - } else { - toReturn = toReturn.concatWith(content.lift(new FlushSelectorOperator(flushSelector, connection))); - } - } - - if (!hasTrailers) { - toReturn = toReturn.concatWith(Observable.just(LastHttpContent.EMPTY_LAST_CONTENT)); - } - - return toReturn; - } - - private HttpRequest _copyHeaders() { - return _copyHeaders(headers.uri(), headers.method()); - } - - private HttpRequest _copyHeaders(String uri, HttpMethod method) { - final HttpRequest newHeaders = new DefaultHttpRequest(headers.protocolVersion(), method, uri); - // TODO: May be we can optimize this by not copying - for (Entry header : headers.headers()) { - newHeaders.headers().set(header.getKey(), header.getValue()); - } - return newHeaders; - } - - public static RawRequest create(HttpVersion version, HttpMethod httpMethod, String uri, - Redirector redirectHandler) { - final HttpRequest headers = new DefaultHttpRequest(version, httpMethod, uri); - return create(headers, null, null, false, redirectHandler); - } - - @SuppressWarnings("rawtypes") - public static RawRequest create(HttpRequest headers, Observable content, boolean hasTrailers, - Redirector redirectHandler) { - return create(headers, content, null, hasTrailers, redirectHandler); - } - - @SuppressWarnings("rawtypes") - public static RawRequest create(HttpRequest headers, Observable content, - Func1 flushSelector, boolean hasTrailers, - Redirector redirectHandler) { - return new RawRequest<>(headers, content, flushSelector, hasTrailers, redirectHandler); - } - - public HttpRequest getHeaders() { - return headers; - } - - @SuppressWarnings("rawtypes") - public Observable getContent() { - return content; - } - - public Func1 getFlushSelector() { - return flushSelector; - } - - public boolean hasTrailers() { - return hasTrailers; - } - - public Redirector getRedirector() { - return redirector; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/Redirector.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/Redirector.java deleted file mode 100644 index 8e7974c..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/Redirector.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client.internal; - -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.reactivex.netty.internal.VoidToAnythingCast; -import io.reactivex.netty.protocol.http.client.HttpClientRequest; -import io.reactivex.netty.protocol.http.client.HttpClientResponse; -import io.reactivex.netty.protocol.http.client.HttpRedirectException; -import io.reactivex.netty.protocol.tcp.client.TcpClient; -import rx.Observable; -import rx.functions.Func1; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static io.reactivex.netty.protocol.http.client.HttpRedirectException.Reason.*; - -public class Redirector implements Func1, Observable>> { - - public static final int DEFAULT_MAX_REDIRECTS = 5; - - private static final Logger logger = Logger.getLogger(Redirector.class.getName()); - - private static final int[] REDIRECTABLE_STATUS_CODES = {301, 302, 303, 307, 308}; - - static { - Arrays.sort(REDIRECTABLE_STATUS_CODES); // Required as we do binary search. This is a safety net in case the - // array is modified (code change) & is not sorted. - } - - private final List visitedLocations; // Is never updated concurrently as redirects are sequential. - private final int maxHops; - private final AtomicInteger redirectCount; // Can be shared across multiple event loops, so needs to be thread-safe. - private volatile HttpResponseStatus lastRedirectStatus; - private final TcpClient> client; - - private RawRequest originalRequest; - - public Redirector(int maxHops, TcpClient> client) { - this.maxHops = maxHops; - this.client = client; - visitedLocations = new ArrayList<>(); - redirectCount = new AtomicInteger(); - } - - public Redirector(TcpClient> client) { - this(DEFAULT_MAX_REDIRECTS, client); - } - - public void setOriginalRequest(RawRequest originalRequest) { - if (null != this.originalRequest) { - throw new IllegalStateException("Original request is already set."); - } - this.originalRequest = originalRequest; - visitedLocations.add(originalRequest.getHeaders().uri()); - } - - @Override - public Observable> call(HttpClientResponse response) { - - Observable> toReturn; - - if (null == originalRequest) { - toReturn = Observable.error(new IllegalStateException("Raw request not available to the redirector.")); - } else if (requiresRedirect(response)) { - String location = extractRedirectLocation(response); - - if (location == null) { - toReturn = Observable.error(new HttpRedirectException(InvalidRedirect, "No redirect location found.")); - } else if (visitedLocations.contains(location)) { - // this forms a loop - toReturn = Observable.error(new HttpRedirectException(RedirectLoop, - "Redirection contains a loop. Last requested location: " - + location)); - } else if (redirectCount.get() >= maxHops) { - toReturn = Observable.error(new HttpRedirectException(TooManyRedirects, - "Too many redirects. Max redirects: " + maxHops)); - } else { - URI redirectUri; - - try { - redirectUri = new URI(location); - - lastRedirectStatus = response.getStatus(); - - redirectCount.incrementAndGet(); - - toReturn = createRedirectRequest(originalRequest, redirectUri, lastRedirectStatus.code()); - } catch (Exception e) { - toReturn = Observable.error(new HttpRedirectException(InvalidRedirect, - "Location is not a valid URI. Provided location: " - + location, e)); - } - } - - } else { - return Observable.just(response); - } - - return response.discardContent() - .map(new VoidToAnythingCast>()) - .ignoreElements() - .concatWith(toReturn); - - - } - - public boolean requiresRedirect(HttpClientResponse response) { - int statusCode = response.getStatus().code(); - boolean requiresRedirect = false; - // This class only supports relative redirects as an HttpClient is always tied to a host:port combo and hence - // can not do an absolute redirect. - if (Arrays.binarySearch(REDIRECTABLE_STATUS_CODES, statusCode) >= 0) { - String location = extractRedirectLocation(response); - // Only process relative URIs: Issue https://github.com/ReactiveX/RxNetty/issues/270 - requiresRedirect = null == location || !location.startsWith("http"); - } - - if (requiresRedirect && statusCode != HttpResponseStatus.SEE_OTHER.code()) { - HttpMethod originalMethod = originalRequest.getHeaders().method(); - // If the Method is not HEAD/GET do not auto redirect - requiresRedirect = originalMethod == HttpMethod.GET || originalMethod == HttpMethod.HEAD; - } - - return requiresRedirect; - } - - protected String extractRedirectLocation(HttpClientResponse redirectedResponse) { - return redirectedResponse.getHeader(HttpHeaderNames.LOCATION); - } - - protected HttpClientRequest createRedirectRequest(RawRequest original, URI redirectLocation, - int redirectStatus) { - - String redirectUri = getNettyRequestUri(redirectLocation, original.getHeaders().uri(), redirectStatus); - - RawRequest redirectRequest = original.setUri(redirectUri); - - if (redirectStatus == 303) { - // according to HTTP spec, 303 mandates the change of request type to GET - // If it is a get, then the content is not to be sent. - redirectRequest = RawRequest.create(redirectRequest.getHeaders().protocolVersion(), HttpMethod.GET, - redirectUri, this); - } - - return HttpClientRequestImpl.create(redirectRequest, client); - } - - protected static String getNettyRequestUri(URI uri, String originalUriString, int redirectStatus) { - StringBuilder sb = new StringBuilder(); - if (uri.getRawPath() != null) { - sb.append(uri.getRawPath()); - } - if (uri.getRawQuery() != null) { - sb.append('?').append(uri.getRawQuery()); - } - if (uri.getRawFragment() != null) { - sb.append('#').append(uri.getRawFragment()); - } else if(redirectStatus >= 300) { - // http://tools.ietf.org/html/rfc7231#section-7.1.2 suggests that the URI fragment should be carried over to - // the redirect location if not exists in the redirect location. - // Issue: https://github.com/ReactiveX/RxNetty/issues/271 - try { - URI originalUri = new URI(originalUriString); - if (originalUri.getRawFragment() != null) { - sb.append('#').append(originalUri.getRawFragment()); - } - } catch (URISyntaxException e) { - logger.log(Level.WARNING, "Error parsing original request URI during redirect. " + - "This means that the path fragment if any in the original request will not be inherited " + - "by the redirect.", e); - } - } - return sb.toString(); - } - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/UnusableConnection.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/UnusableConnection.java deleted file mode 100644 index 9a105bf..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/internal/UnusableConnection.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client.internal; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.FileRegion; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.concurrent.EventExecutorGroup; -import io.reactivex.netty.channel.AllocatingTransformer; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.events.ConnectionEventListener; -import io.reactivex.netty.events.EventPublisher; -import rx.Observable; -import rx.Observable.Transformer; -import rx.functions.Action1; -import rx.functions.Func1; - -final class UnusableConnection extends Connection { - - protected UnusableConnection(Channel nettyChannel, - ConnectionEventListener eventListener, - EventPublisher eventPublisher) { - super(nettyChannel); - } - - @Override - public Observable write(Observable msgs) { - return Observable.error(new IllegalStateException("Connection is not usable.")); - } - - @Override - public Observable write(Observable msgs, Func1 flushSelector) { - return Observable.error(new IllegalStateException("Connection is not usable.")); - } - - @Override - public Observable writeAndFlushOnEach(Observable msgs) { - return Observable.error(new IllegalStateException("Connection is not usable.")); - } - - @Override - public Observable writeString(Observable msgs) { - return Observable.error(new IllegalStateException("Connection is not usable.")); - } - - @Override - public Observable writeString(Observable msgs, Func1 flushSelector) { - return Observable.error(new IllegalStateException("Connection is not usable.")); - } - - @Override - public Observable writeStringAndFlushOnEach(Observable msgs) { - return Observable.error(new IllegalStateException("Connection is not usable.")); - } - - @Override - public Observable writeBytes(Observable msgs) { - return Observable.error(new IllegalStateException("Connection is not usable.")); - } - - @Override - public Observable writeBytes(Observable msgs, Func1 flushSelector) { - return Observable.error(new IllegalStateException("Connection is not usable.")); - } - - @Override - public Observable writeBytesAndFlushOnEach(Observable msgs) { - return Observable.error(new IllegalStateException("Connection is not usable.")); - } - - @Override - public Observable writeFileRegion(Observable msgs) { - return Observable.error(new IllegalStateException("Connection is not usable.")); - } - - @Override - public Observable writeFileRegion(Observable msgs, Func1 flushSelector) { - return Observable.error(new IllegalStateException("Connection is not usable.")); - } - - @Override - public Observable writeFileRegionAndFlushOnEach(Observable msgs) { - return Observable.error(new IllegalStateException("Connection is not usable.")); - } - - @Override - public void flush() { - throw new IllegalStateException("Connection is not usable."); - } - - @Override - public Observable close() { - return Observable.error(new IllegalStateException("Connection is not usable.")); - } - - @Override - public Observable close(boolean flush) { - return Observable.error(new IllegalStateException("Connection is not usable.")); - } - - @Override - public void closeNow() { - throw new IllegalStateException("Connection is not usable."); - } - - @Override - public Observable closeListener() { - throw new IllegalStateException("Connection is not usable."); - } - - public static Connection create() { - return new UnusableConnection<>(new EmbeddedChannel(), null, null); - } - - @Override - public Connection addChannelHandlerFirst(String name, ChannelHandler handler) { - return cast(); - } - - @Override - public Connection addChannelHandlerFirst(EventExecutorGroup group, String name, - ChannelHandler handler) { - return cast(); - } - - @Override - public Connection addChannelHandlerLast(String name, ChannelHandler handler) { - return cast(); - } - - @Override - public Connection addChannelHandlerLast(EventExecutorGroup group, String name, - ChannelHandler handler) { - return cast(); - } - - @Override - public Connection addChannelHandlerBefore(String baseName, String name, ChannelHandler handler) { - return cast(); - } - - @Override - public Connection addChannelHandlerBefore(EventExecutorGroup group, String baseName, String name, - ChannelHandler handler) { - return cast(); - } - - @Override - public Connection addChannelHandlerAfter(String baseName, String name, ChannelHandler handler) { - return cast(); - } - - @Override - public Connection addChannelHandlerAfter(EventExecutorGroup group, String baseName, String name, - ChannelHandler handler) { - return cast(); - } - - @Override - public Connection pipelineConfigurator(Action1 pipelineConfigurator) { - return cast(); - } - - @Override - public Connection transformRead(Transformer transformer) { - throw new IllegalStateException("Connection is not usable."); - } - - @Override - public Connection transformWrite(AllocatingTransformer transformer) { - throw new IllegalStateException("Connection is not usable."); - } - - @SuppressWarnings("unchecked") - private Connection cast() { - return (Connection) this; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/loadbalancer/EWMABasedP2CStrategy.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/loadbalancer/EWMABasedP2CStrategy.java deleted file mode 100644 index e2ce4dc..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/client/loadbalancer/EWMABasedP2CStrategy.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client.loadbalancer; - -import io.reactivex.netty.client.Host; -import io.reactivex.netty.client.events.ClientEventListener; -import io.reactivex.netty.client.loadbalancer.AbstractP2CStrategy; -import io.reactivex.netty.protocol.http.client.events.HttpClientEventsListener; -import io.reactivex.netty.protocol.http.client.loadbalancer.EWMABasedP2CStrategy.HttpClientListenerImpl; - -import java.util.concurrent.TimeUnit; - -import static java.util.concurrent.TimeUnit.NANOSECONDS; -import static java.util.concurrent.TimeUnit.SECONDS; - -public class EWMABasedP2CStrategy extends AbstractP2CStrategy { - private static final double STARTUP_PENALTY = Long.MAX_VALUE >> 12; - private final double tauUp; - private final double tauDown; - private double penaltyOnConnectionFailure; - private double penaltyOn503; - - public EWMABasedP2CStrategy(double tauUp, double tauDown, double penaltyOnConnectionFailure, - double penaltyOn503) { - this.tauUp = tauUp; - this.tauDown = tauDown; - this.penaltyOnConnectionFailure = penaltyOnConnectionFailure; - this.penaltyOn503 = penaltyOn503; - } - - public EWMABasedP2CStrategy() { - this(NANOSECONDS.convert(1, SECONDS), NANOSECONDS.convert(15, SECONDS), 2, 5); - } - - @Override - protected HttpClientListenerImpl newListener(Host host) { - return new HttpClientListenerImpl(); - } - - @Override - protected double getWeight(ClientEventListener listener) { - return ((HttpClientListenerImpl) listener).getWeight(); - } - - public class HttpClientListenerImpl extends HttpClientEventsListener { - private final long epoch = System.nanoTime(); - private long stamp = epoch; // last timestamp in nanos we observed an rtt - private int pending = 0; // instantaneous rate - private double cost = 0.0; // ewma of rtt, sensitive to peaks. - - public double getWeight() { - observe(0.0); - if (cost == 0.0 && pending != 0) { - return STARTUP_PENALTY + pending; - } else { - return cost * (pending+1); - } - } - - @Override - public synchronized void onRequestWriteComplete(long duration, TimeUnit timeUnit) { - pending += 1; - } - - @Override - public synchronized void onResponseReceiveComplete(long duration, TimeUnit timeUnit) { - pending -= 1; - observe(NANOSECONDS.convert(duration, timeUnit)); - } - - @Override - public void onResponseHeadersReceived(int responseCode, long duration, TimeUnit timeUnit) { - if (responseCode == 503) { - observe(TimeUnit.NANOSECONDS.convert(duration, timeUnit) * penaltyOn503); - } - } - - @Override - public void onConnectFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - observe(TimeUnit.NANOSECONDS.convert(duration, timeUnit) * penaltyOnConnectionFailure); - } - - private void observe(double rtt) { - long t = System.nanoTime(); - long td = Math.max(t - stamp, 0L); - if (rtt > cost) { - double w = Math.exp(-td / tauUp); - cost = cost * w + rtt * (1.0 - w); - } else { - double w = Math.exp(-td / tauDown); - cost = cost * w + rtt * (1.0 - w); - } - stamp = t; - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/AbstractHttpConnectionBridge.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/AbstractHttpConnectionBridge.java deleted file mode 100644 index d6a4278..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/AbstractHttpConnectionBridge.java +++ /dev/null @@ -1,555 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.internal; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.channel.Channel; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.util.AttributeKey; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.RecyclableArrayList; -import io.reactivex.netty.channel.AppendTransformerEvent; -import io.reactivex.netty.channel.ConnectionInputSubscriberEvent; -import io.reactivex.netty.channel.ConnectionInputSubscriberReplaceEvent; -import io.reactivex.netty.channel.SubscriberToChannelFutureBridge; -import io.reactivex.netty.channel.WriteTransformations; -import io.reactivex.netty.client.ClientConnectionToChannelBridge.ConnectionReuseEvent; -import io.reactivex.netty.events.Clock; -import io.reactivex.netty.protocol.http.internal.AbstractHttpConnectionBridge.State.Stage; -import rx.Producer; -import rx.Subscriber; -import rx.functions.Action0; -import rx.subscriptions.Subscriptions; - -import java.nio.channels.ClosedChannelException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; -import static io.netty.handler.codec.http.HttpHeaderValues.*; - -public abstract class AbstractHttpConnectionBridge extends ChannelDuplexHandler { - - private static final Logger logger = Logger.getLogger(AbstractHttpConnectionBridge.class.getName()); - - public static final AttributeKey CONNECTION_UPGRADED = - AttributeKey.valueOf("rxnetty_http_upgraded_connection"); - - @SuppressWarnings("ThrowableInstanceNeverThrown") - private static final IllegalStateException ONLY_ONE_CONTENT_INPUT_SUB_ALLOWED = - new IllegalStateException("Only one subscriber allowed for HTTP content."); - @SuppressWarnings("ThrowableInstanceNeverThrown") - private static final IllegalStateException LAZY_CONTENT_INPUT_SUB = - new IllegalStateException("Channel is set to auto-read but the subscription was lazy."); - @SuppressWarnings("ThrowableInstanceNeverThrown") - private static final IllegalStateException CONTENT_ARRIVED_WITH_NO_SUB = - new IllegalStateException("HTTP Content received but no subscriber was registered."); - @SuppressWarnings("ThrowableInstanceNeverThrown") - private static final ClosedChannelException CLOSED_CHANNEL_EXCEPTION = new ClosedChannelException(); - - static { - ONLY_ONE_CONTENT_INPUT_SUB_ALLOWED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE); - LAZY_CONTENT_INPUT_SUB.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE); - CONTENT_ARRIVED_WITH_NO_SUB.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE); - CLOSED_CHANNEL_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE); - } - - protected ConnectionInputSubscriber connectionInputSubscriber; - private final UnsafeEmptySubscriber emptyContentSubscriber; - private final WriteTransformations transformations; - private long headerWriteStartTimeNanos; - - protected AbstractHttpConnectionBridge() { - emptyContentSubscriber = new UnsafeEmptySubscriber<>("Error while waiting for HTTP content."); - transformations = new WriteTransformations(); - } - - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - - boolean skipNextHandlers = false; - if (isOutboundHeader(msg)) { - /*Reset on every header write, when we support pipelining, this should be a queue.*/ - headerWriteStartTimeNanos = Clock.newStartTimeNanos(); - HttpMessage httpMsg = (HttpMessage) msg; - if (!HttpUtil.isContentLengthSet(httpMsg) && !HttpVersion.HTTP_1_0.equals(httpMsg.protocolVersion())) { - // If there is no content length we need to specify the transfer encoding as chunked as we always - // send data in multiple HttpContent. - // On the other hand, if someone wants to not have chunked encoding, adding content-length will work - // as expected. - httpMsg.headers().set(TRANSFER_ENCODING, CHUNKED); - } - - beforeOutboundHeaderWrite(httpMsg, promise, headerWriteStartTimeNanos); - - } else if (msg instanceof LastHttpContent) { - onOutboundLastContentWrite((LastHttpContent) msg, promise, headerWriteStartTimeNanos); - } else if (transformations.acceptMessage(msg)) { - RecyclableArrayList out = RecyclableArrayList.newInstance(); - try { - transformations.transform(msg, ctx.alloc(), out); - } finally { - final int sizeMinusOne = out.size() - 1; - if (sizeMinusOne == 0) { - ctx.write(out.get(0), promise); - } else if (sizeMinusOne > 0) { - ChannelPromise voidPromise = ctx.voidPromise(); - boolean isVoidPromise = promise == voidPromise; - for (int i = 0; i < sizeMinusOne; i ++) { - ChannelPromise p; - if (isVoidPromise) { - p = voidPromise; - } else { - p = ctx.newPromise(); - } - ctx.write(out.get(i), p); - } - ctx.write(out.get(sizeMinusOne), promise); - } - out.recycle(); - skipNextHandlers = true; - } - } - - if (!skipNextHandlers) { - super.write(ctx, msg, promise); - } - } - - protected abstract void beforeOutboundHeaderWrite(HttpMessage httpMsg, ChannelPromise promise, long startTimeNanos); - - protected abstract void onOutboundLastContentWrite(LastHttpContent msg, ChannelPromise promise, - long headerWriteStartTimeNanos); - - @Override - public void userEventTriggered(final ChannelHandlerContext ctx, Object evt) throws Exception { - - Object eventToPropagateFurther = evt; - Boolean connUpgradedAttr = ctx.channel().attr(CONNECTION_UPGRADED).get(); - boolean connUpgraded = null != connUpgradedAttr ? connUpgradedAttr : false; - - if (evt instanceof ConnectionInputSubscriberEvent) { - - @SuppressWarnings({ "unchecked", "rawtypes" }) - ConnectionInputSubscriberEvent orig = (ConnectionInputSubscriberEvent) evt; - - if (!connUpgraded) { - /*Local copy to refer from the channel close listener. As the instance level copy can change*/ - @SuppressWarnings("unchecked") - final ConnectionInputSubscriber _connectionInputSubscriber = newConnectionInputSubscriber(orig, - ctx.channel()); - - connectionInputSubscriber = _connectionInputSubscriber; - - final SubscriberToChannelFutureBridge l = new SubscriberToChannelFutureBridge() { - - @Override - protected void doOnSuccess(ChannelFuture future) { - onChannelClose(_connectionInputSubscriber); - } - - @Override - protected void doOnFailure(ChannelFuture future, Throwable cause) { - onChannelClose(_connectionInputSubscriber); - } - }; - - l.bridge(ctx.channel().closeFuture(), _connectionInputSubscriber); - - @SuppressWarnings({ "unchecked", "rawtypes" }) - ConnectionInputSubscriberEvent newEvent = new ConnectionInputSubscriberEvent(_connectionInputSubscriber - ); - eventToPropagateFurther = newEvent; - } else { - if (null != connectionInputSubscriber) { - connectionInputSubscriber.state.stage = Stage.Upgraded; - } - @SuppressWarnings({ "unchecked", "rawtypes" }) - ConnectionInputSubscriberReplaceEvent replaceEvt = new ConnectionInputSubscriberReplaceEvent<>(orig); - eventToPropagateFurther = replaceEvt; - } - } else if (evt instanceof HttpContentSubscriberEvent) { - newHttpContentSubscriber(evt, connectionInputSubscriber); - } else if (evt instanceof AppendTransformerEvent) { - transformations.appendTransformer(((AppendTransformerEvent)evt).getTransformer()); - } else if (evt instanceof ConnectionReuseEvent) { - transformations.resetTransformations(); - } - - super.userEventTriggered(ctx, eventToPropagateFurther); - } - - protected ConnectionInputSubscriber newConnectionInputSubscriber(ConnectionInputSubscriberEvent orig, - Channel channel) { - ConnectionInputSubscriber toReturn = new ConnectionInputSubscriber(orig.getSubscriber(), channel); - toReturn.state.headerSub.add(Subscriptions.create(toReturn)); - return toReturn; - } - - protected final void onChannelClose(ConnectionInputSubscriber connectionInputSubscriber) { - /* - * If any of the subscribers(header or content) are still subscribed and the channel is closed, it is an - * error. If they are unsubscribed, this will be a no-op. - */ - connectionInputSubscriber.onError(CLOSED_CHANNEL_EXCEPTION); - } - - protected void onClosedBeforeReceiveComplete(Channel channel) { - // No Op. Override to add behavior - } - - protected void resetSubscriptionState(final ConnectionInputSubscriber connectionInputSubscriber) { - connectionInputSubscriber.resetSubscribers(); - } - - protected abstract boolean isInboundHeader(Object nextItem); - - protected abstract boolean isOutboundHeader(Object nextItem); - - protected abstract Object newHttpObject(Object nextItem, Channel channel); - - protected abstract void onContentReceived(); - - protected abstract void onContentReceiveComplete(long receiveStartTimeNanos); - - protected void onNewContentSubscriber(ConnectionInputSubscriber inputSubscriber, Subscriber newSub) { - // No Op. - } - - protected long getHeaderWriteStartTimeNanos() { - return headerWriteStartTimeNanos; - } - - private void processNextItemInEventloop(Object nextItem, ConnectionInputSubscriber connectionInputSubscriber) { - final State state = connectionInputSubscriber.state; - final Channel channel = connectionInputSubscriber.channel; - - if (isInboundHeader(nextItem)) { - state.headerReceived(); - Object newHttpObject = newHttpObject(nextItem, channel); - connectionInputSubscriber.nextHeader(newHttpObject); - /*Why not complete the header sub? It may be listening to multiple responses (pipelining)*/ - checkEagerSubscriptionIfConfigured(channel, state); - - final HttpObject httpObject = (HttpObject) nextItem; - if (httpObject.decoderResult().isFailure()) { - connectionInputSubscriber.onError(httpObject.decoderResult().cause()); - channel.close();// Netty rejects all data after decode failure, so closing connection - // Issue: https://github.com/netty/netty/issues/3362 - } - } - - if (nextItem instanceof HttpContent) { - onContentReceived(); - ByteBuf content = ((ByteBufHolder) nextItem).content(); - if (nextItem instanceof LastHttpContent) { - /* - * Since, LastHttpContent is always received, even if the pipeline does not emit ByteBuf, if - * ByteBuf with the LastHttpContent is empty, only trailing headers are emitted. Otherwise, - * the content type should be a ByteBuf. - */ - if (content.isReadable()) { - connectionInputSubscriber.nextContent(content); - } else { - /*Since, the content buffer, was not sent, release it*/ - ReferenceCountUtil.release(content); - } - state.contentComplete(); - connectionInputSubscriber.contentComplete(); - onContentReceiveComplete(state.headerReceivedTimeNanos); - } else { - connectionInputSubscriber.nextContent(content); - } - } else if(!isInboundHeader(nextItem)){ - connectionInputSubscriber.nextContent(nextItem); - } - } - - private void newHttpContentSubscriber(final Object evt, final ConnectionInputSubscriber inputSubscriber) { - @SuppressWarnings("unchecked") - HttpContentSubscriberEvent contentSubscriberEvent = (HttpContentSubscriberEvent) evt; - Subscriber newSub = contentSubscriberEvent.getSubscriber(); - Throwable errorToRaise = null; - - if (null == inputSubscriber) { - errorToRaise = new NullPointerException("Null Connection input subscriber."); - } else { - final State state = inputSubscriber.state; - - if (state.raiseErrorOnInputSubscription()) { - errorToRaise = state.raiseErrorOnInputSubscription; - } else if (isValidToEmit(state.contentSub)) { - /*Allow only one concurrent input subscriber but allow concatenated subscribers*/ - if (!newSub.isUnsubscribed()) { - errorToRaise = ONLY_ONE_CONTENT_INPUT_SUB_ALLOWED; - } - } else if (state.stage == Stage.HeaderReceived) { - inputSubscriber.setupContentSubscriber(newSub); - onNewContentSubscriber(inputSubscriber, newSub); - } else { - errorToRaise = new IllegalStateException("Content subscription received without request start."); - } - } - - if (null != errorToRaise && isValidToEmit(newSub)) { - newSub.onError(errorToRaise); - } - } - - private void checkEagerSubscriptionIfConfigured(Channel channel, final State state) { - if (channel.config().isAutoRead()) { - if (null == state.contentSub) { - // If the channel is set to auto-read and there is no eager subscription then, we should raise errors - // when a subscriber arrives. - state.raiseErrorOnInputSubscription = LAZY_CONTENT_INPUT_SUB; - state.contentSub = emptyContentSubscriber; - } - } - } - - private static boolean isValidToEmit(Subscriber subscriber) { - return null != subscriber && !subscriber.isUnsubscribed(); - } - - /** - * All state for this handler. At any point we need to invoke any method outside of this handler, this state should - * be stored in a local variable and used after the external call finishes. Failure to do so will cause race - * conditions in us using different state before and after the method call specifically if the external call ends - * up generating a user generated event and triggering {@link #userEventTriggered(ChannelHandlerContext, Object)} - * which in turn changes this state. - * - * Issue: https://github.com/Netflix/RxNetty/issues/129 - */ - protected static final class State { - - /*Visible for testing*/enum Stage { - /*Strictly in the order in which the transitions would happen*/ - Created, - HeaderReceived, - ContentComplete, - Upgraded - } - - protected IllegalStateException raiseErrorOnInputSubscription; - @SuppressWarnings("rawtypes") private Subscriber headerSub; - @SuppressWarnings("rawtypes") private Subscriber contentSub; - private long headerReceivedTimeNanos; - - private volatile Stage stage = Stage.Created; - - /*Visible for testing*/void headerReceived() { - headerReceivedTimeNanos = Clock.newStartTimeNanos(); - stage = Stage.HeaderReceived; - } - - private void contentComplete() { - stage = Stage.ContentComplete; - } - - public boolean raiseErrorOnInputSubscription() { - return null != raiseErrorOnInputSubscription; - } - - public boolean startButNotCompleted() { - return stage == Stage.HeaderReceived; - } - - public boolean receiveStarted() { - return stage.ordinal() > Stage.Created.ordinal(); - } - - /*Visible for testing*/Subscriber getHeaderSub() { - return headerSub; - } - - /*Visible for testing*/Subscriber getContentSub() { - return contentSub; - } - } - - protected class ConnectionInputSubscriber extends Subscriber implements Action0, Runnable { - - private final Channel channel; - private final State state; - private Producer producer; - - @SuppressWarnings("rawtypes") - private ConnectionInputSubscriber(Subscriber subscriber, Channel channel) { - state = new State(); - this.channel = channel; - state.headerSub = subscriber; - } - - @Override - public void onCompleted() { - // This means channel input has completed - if (state.startButNotCompleted()) { - onError(CLOSED_CHANNEL_EXCEPTION); - } else { - completeAllSubs(); - } - } - - @Override - public void onError(Throwable e) { - // This means channel input has got an error & hence no other notifications will arrive. - errorAllSubs(e); - - if (state.startButNotCompleted()) { - onClosedBeforeReceiveComplete(channel); - } - } - - @Override - public void onNext(final Object next) { - if (channel.eventLoop().inEventLoop()) { - processNextItemInEventloop(next, this); - } else { - channel.eventLoop().execute(new Runnable() { - @Override - public void run() { - processNextItemInEventloop(next, ConnectionInputSubscriber.this); - } - }); - } - } - - @Override - public void setProducer(Producer producer) { - this.producer = producer; - state.headerSub.setProducer(producer); /*Content & trailer producers are set on subscription*/ - } - - public Channel getChannel() { - return channel; - } - - public void resetSubscribers() { - completeAllSubs(); - } - - private void completeAllSubs() { - if (isValidToEmit(state.headerSub)) { - state.headerSub.onCompleted(); - } - if (isValidToEmit(state.contentSub)) { - state.contentSub.onCompleted(); - } - } - - private void errorAllSubs(Throwable throwable) { - if (isValidToEmit(state.headerSub)) { - state.headerSub.onError(throwable); - } - if (isValidToEmit(state.contentSub)) { - state.contentSub.onError(throwable); - } - } - - @SuppressWarnings("unchecked") - private void nextContent(final Object nextObject) { - if (isValidToEmit(state.contentSub)) { - state.contentSub.onNext(nextObject); - } else { - contentArrivedWhenSubscriberNotValid(); - if (logger.isLoggable(Level.WARNING)) { - logger.log(Level.WARNING, "Data received on channel, but no subscriber registered. Discarding data. Message class: " - + nextObject.getClass().getName() + ", channel: " + channel); - } - ReferenceCountUtil.release(nextObject); - } - } - - @SuppressWarnings("unchecked") - private void nextHeader(final Object nextObject) { - if (isValidToEmit(state.headerSub)) { - state.headerSub.onNext(nextObject); - } - } - - private void setupContentSubscriber(Subscriber newSub) { - - assert channel.eventLoop().inEventLoop(); - - state.contentSub = newSub; - state.contentSub.add(Subscriptions.create(this)); - state.contentSub.setProducer(producer); /*Content demand matches upstream demand*/ - } - - public void contentComplete() { - assert channel.eventLoop().inEventLoop(); - - if (isValidToEmit(state.contentSub)) { - state.contentSub.onCompleted(); - } else { - contentArrivedWhenSubscriberNotValid(); - } - } - - private void contentArrivedWhenSubscriberNotValid() { - if (null == state.contentSub) { - /* - * Cases when auto-read is off and there is lazy subscription, due to mismatched request demands on the - * subscriber, it may so happen that we get content without a subscriber, in such cases, we should raise - * an error. - */ - state.raiseErrorOnInputSubscription = CONTENT_ARRIVED_WITH_NO_SUB; - } - } - - /*Visible for testing*/State getState() { - return state; - } - - @Override - public void run() { - if (state.contentSub != null) { - if (state.contentSub.isUnsubscribed()) { - // Content sub exists and unsubscribed, so unsubscribe from input. - unsubscribe(); - } else if (state.headerSub.isUnsubscribed() && !state.receiveStarted()) { - // Header sub unsubscribed before request started, unsubscribe from input. - unsubscribe(); - } - } else if (state.headerSub.isUnsubscribed() && !state.receiveStarted()) { - // Header sub unsubscribed before request started, unsubscribe from input. - unsubscribe(); - } - } - - @Override - public void call() { - if (channel.eventLoop().inEventLoop()) { - run(); - } else { - channel.eventLoop().execute(this); - } - } - - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/HttpContentSubscriberEvent.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/HttpContentSubscriberEvent.java deleted file mode 100644 index 7f2a93b..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/HttpContentSubscriberEvent.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.internal; - -import rx.Subscriber; - -public class HttpContentSubscriberEvent { - - private final Subscriber subscriber; - - public HttpContentSubscriberEvent(Subscriber subscriber) { - this.subscriber = subscriber; - } - - public Subscriber getSubscriber() { - return subscriber; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/HttpMessageFormatter.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/HttpMessageFormatter.java deleted file mode 100644 index a733546..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/HttpMessageFormatter.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.internal; - -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; - -import java.util.Iterator; -import java.util.Map.Entry; - -public final class HttpMessageFormatter { - - private HttpMessageFormatter() { - } - - public static String formatRequest(HttpVersion version,HttpMethod method, String uri, - Iterator> headers) { - StringBuilder builder = new StringBuilder(); - builder.append(method) - .append(' ') - .append(uri) - .append(' ') - .append(version.text()) - .append('\n'); - - printHeaders(headers, builder); - - return builder.toString(); - } - - public static String formatResponse(HttpVersion version, HttpResponseStatus status, - Iterator> headers) { - StringBuilder builder = new StringBuilder(); - builder.append(version.text()) - .append(' ') - .append(status.code()) - .append(' ') - .append(status.reasonPhrase()) - .append('\n'); - - printHeaders(headers, builder); - - return builder.toString(); - } - - private static void printHeaders(Iterator> headers, StringBuilder builder) { - while (headers.hasNext()) { - Entry next = headers.next(); - builder.append(next.getKey()) - .append(": ") - .append(next.getValue()) - .append('\n'); - } - - builder.append('\n'); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/OperatorTrailer.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/OperatorTrailer.java deleted file mode 100644 index 095ac5a..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/OperatorTrailer.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.internal; - -import io.reactivex.netty.protocol.http.TrailingHeaders; -import rx.Observable; -import rx.Observable.Operator; -import rx.Subscriber; -import rx.exceptions.Exceptions; -import rx.exceptions.OnErrorThrowable; -import rx.functions.Func0; -import rx.functions.Func2; - -@SuppressWarnings({"rawtypes", "unchecked"}) -public class OperatorTrailer implements Operator { - - private final Func0 trailerFactory; - private final Func2 trailerMutator; - - public OperatorTrailer(Func0 trailerFactory, Func2 trailerMutator) { - this.trailerFactory = trailerFactory; - this.trailerMutator = trailerMutator; - } - - @Override - public Object call(Object child) { - final Subscriber subscriber = (Subscriber) child; - return new Subscriber(subscriber) { - - private T trailer = trailerFactory.call(); - - @SuppressWarnings("unchecked") - @Override - public void onCompleted() { - subscriber.onNext(trailer); - subscriber.onCompleted(); - } - - @Override - public void onError(Throwable e) { - subscriber.onError(e); - } - - @SuppressWarnings("unchecked") - @Override - public void onNext(Object i) { - try { - trailer = (T) trailerMutator.call(trailer, i); - subscriber.onNext(i); - } catch (Throwable e) { - Exceptions.throwIfFatal(e); - onError(OnErrorThrowable.addValueAsLastCause(e, i)); - } - } - }; - } - - @SuppressWarnings("unchecked") - public static Observable liftFrom(Observable source, - Func0 trailerFactory, Func2 trailerMutator) { - return source.lift(new OperatorTrailer<>(trailerFactory, trailerMutator)); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/UnsafeEmptySubscriber.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/UnsafeEmptySubscriber.java deleted file mode 100644 index b4d7db7..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/internal/UnsafeEmptySubscriber.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.internal; - -import io.netty.util.ReferenceCountUtil; -import rx.Subscriber; -import rx.observers.SafeSubscriber; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A subscriber that can be reused if and only if not wrapped in a {@link SafeSubscriber}. - */ -final class UnsafeEmptySubscriber extends Subscriber { - - private static final Logger logger = Logger.getLogger(UnsafeEmptySubscriber.class.getName()); - - private final String msg; - - protected UnsafeEmptySubscriber(String msg) { - this.msg = msg; - } - - @Override - public void onCompleted() { - } - - @Override - public void onError(Throwable e) { - logger.log(Level.SEVERE, msg, e); - } - - @Override - public void onNext(T o) { - ReferenceCountUtil.release(o); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/ContentWriterImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/ContentWriterImpl.java deleted file mode 100644 index bd9a18c..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/ContentWriterImpl.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.handler.codec.http.DefaultLastHttpContent; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.LastHttpContent; -import io.reactivex.netty.channel.ChannelOperations; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.FlushSelectorOperator; -import io.reactivex.netty.protocol.http.TrailingHeaders; -import io.reactivex.netty.protocol.http.internal.OperatorTrailer; -import rx.Observable; -import rx.Subscriber; -import rx.functions.Func0; -import rx.functions.Func1; -import rx.functions.Func2; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; - -final class ContentWriterImpl extends ResponseContentWriter { - - @SuppressWarnings("rawtypes") - private final Connection connection; - - @SuppressWarnings("rawtypes") - private final Observable headersObservable; - @SuppressWarnings("rawtypes") - private final Observable contentObservable; - - private final HttpResponse headers; - - private final Func1 flushOnEachSelector = new Func1() { - @Override - public Boolean call(C w) { - return true; - } - }; - - ContentWriterImpl(@SuppressWarnings("rawtypes") final Connection connection, final HttpResponse headers) { - super(new OnSubscribe() { - @SuppressWarnings("unchecked") - @Override - public void call(Subscriber subscriber) { - /*We are never sending content as the subscription is to the headers only writer.*/ - if (!HttpUtil.isTransferEncodingChunked(headers)) { - headers.headers().set(CONTENT_LENGTH, 0); - } - connection.write(Observable.just(headers)).unsafeSubscribe(subscriber); - } - }); - this.connection = connection; - this.headers = headers; - headersObservable = Observable.just(headers); - contentObservable = null; - } - - private ContentWriterImpl(final ContentWriterImpl parent, - @SuppressWarnings("rawtypes") final Observable content, final boolean appendTrailer) { - super(new OnSubscribe() { - @SuppressWarnings("unchecked") - @Override - public void call(Subscriber subscriber) { - parent.connection.write(getHttpStream(parent, content, appendTrailer)) - .unsafeSubscribe(subscriber); - } - }); - connection = parent.connection; - headers = parent.headers; - headersObservable = parent.headersObservable; - if (null == parent.contentObservable) { - contentObservable = content; - } else { - @SuppressWarnings({"rawtypes", "unchecked"}) - Observable rawMerged = parent.contentObservable.mergeWith(content); - contentObservable = rawMerged; - } - } - - @Override - public ResponseContentWriter write(Observable msgs) { - return new ContentWriterImpl<>(this, msgs, true); - } - - @Override - public Observable write(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource; - return new ContentWriterImpl<>(this, OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), - false); - } - - @Override - public Observable write(Observable contentSource, Func0 trailerFactory, - Func2 trailerMutator, - Func1 flushSelector) { - return write(contentSource.lift(new FlushSelectorOperator<>(flushSelector, connection)), - trailerFactory, trailerMutator); - } - - @Override - public ResponseContentWriter write(Observable msgs, final Func1 flushSelector) { - return new ContentWriterImpl<>(this, msgs.lift(new FlushSelectorOperator<>(flushSelector, connection)), - true); - } - - @Override - public ResponseContentWriter writeAndFlushOnEach(Observable msgs) { - return write(msgs, flushOnEachSelector); - } - - @Override - public ResponseContentWriter writeString(Observable msgs) { - return new ContentWriterImpl<>(this, msgs, true); - } - - @Override - public Observable writeString(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource; - return new ContentWriterImpl<>(this, OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), - false); - } - - @Override - public Observable writeString(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator, - Func1 flushSelector) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource.lift(new FlushSelectorOperator<>(flushSelector, connection)); - return new ContentWriterImpl<>(this, OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), - false); - } - - @Override - public ResponseContentWriter writeString(Observable msgs, Func1 flushSelector) { - return new ContentWriterImpl<>(this, msgs.lift(new FlushSelectorOperator<>(flushSelector, connection)), - true); - } - - @Override - public ResponseContentWriter writeStringAndFlushOnEach(Observable msgs) { - return writeString(msgs, ChannelOperations.FLUSH_ON_EACH_STRING); - } - - @Override - public ResponseContentWriter writeBytes(Observable msgs) { - return new ContentWriterImpl<>(this, msgs, true); - } - - @Override - public Observable writeBytes(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource; - return new ContentWriterImpl<>(this, OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), - false); - } - - @Override - public Observable writeBytes(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator, - Func1 flushSelector) { - @SuppressWarnings("rawtypes") - Observable rawObservable = contentSource.lift(new FlushSelectorOperator<>(flushSelector, connection)); - return new ContentWriterImpl<>(this, OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), - false); - } - - @Override - public ResponseContentWriter writeBytes(Observable msgs, Func1 flushSelector) { - return new ContentWriterImpl<>(this, msgs.lift(new FlushSelectorOperator<>(flushSelector, connection)), - true); - } - - @Override - public ResponseContentWriter writeBytesAndFlushOnEach(Observable msgs) { - return writeBytes(msgs, ChannelOperations.FLUSH_ON_EACH_BYTES); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - private static Observable getHttpStream(final ContentWriterImpl parent, Observable content, boolean appendTrailer) { - Observable httpStream = parent.headersObservable; - if (null != parent.contentObservable) { - httpStream = httpStream.concatWith(parent.contentObservable.mergeWith(content)); - } else { - httpStream = httpStream.concatWith(content); - } - - if (appendTrailer) { - httpStream = httpStream.concatWith(Observable.just(LastHttpContent.EMPTY_LAST_CONTENT)); - } - - return httpStream; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/FailedContentWriter.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/FailedContentWriter.java deleted file mode 100644 index c367107..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/FailedContentWriter.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.reactivex.netty.protocol.http.TrailingHeaders; -import rx.Observable; -import rx.Subscriber; -import rx.functions.Func0; -import rx.functions.Func1; -import rx.functions.Func2; - -class FailedContentWriter extends ResponseContentWriter { - - FailedContentWriter() { - super(new OnSubscribe() { - @Override - public void call(Subscriber subscriber) { - subscriber.onError(new IllegalStateException("HTTP headers are already sent.")); - } - }); - } - - @Override - public ResponseContentWriter write(Observable msgs) { - return this; - } - - @Override - public Observable write(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator) { - return this; - } - - @Override - public Observable write(Observable contentSource, Func0 trailerFactory, - Func2 trailerMutator, - Func1 flushSelector) { - return this; - } - - @Override - public ResponseContentWriter write(Observable msgs, Func1 flushSelector) { - return this; - } - - @Override - public ResponseContentWriter writeAndFlushOnEach(Observable msgs) { - return this; - } - - @Override - public ResponseContentWriter writeString(Observable msgs) { - return this; - } - - @Override - public Observable writeString(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator) { - return this; - } - - @Override - public Observable writeString(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator, - Func1 flushSelector) { - return this; - } - - @Override - public ResponseContentWriter writeString(Observable msgs, Func1 flushSelector) { - return this; - } - - @Override - public ResponseContentWriter writeStringAndFlushOnEach(Observable msgs) { - return this; - } - - @Override - public ResponseContentWriter writeBytes(Observable msgs) { - return this; - } - - @Override - public Observable writeBytes(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator) { - return this; - } - - @Override - public Observable writeBytes(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator, - Func1 flushSelector) { - return this; - } - - @Override - public ResponseContentWriter writeBytes(Observable msgs, Func1 flushSelector) { - return this; - } - - @Override - public ResponseContentWriter writeBytesAndFlushOnEach(Observable msgs) { - return this; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpConnectionHandler.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpConnectionHandler.java deleted file mode 100644 index 45327a3..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpConnectionHandler.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpVersion; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.events.Clock; -import io.reactivex.netty.protocol.http.server.events.HttpServerEventPublisher; -import io.reactivex.netty.protocol.tcp.server.ConnectionHandler; -import rx.Observable; -import rx.Observable.Operator; -import rx.Subscriber; -import rx.functions.Func1; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; -import static io.netty.handler.codec.http.HttpResponseStatus.*; -import static io.reactivex.netty.events.Clock.*; -import static java.util.concurrent.TimeUnit.*; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class HttpConnectionHandler implements ConnectionHandler, Object> { - - private static final Logger logger = Logger.getLogger(HttpConnectionHandler.class.getName()); - - private final RequestHandler requestHandler; - private final HttpServerEventPublisher eventPublisher; - private final boolean sendHttp10ResponseFor10Request; - - public HttpConnectionHandler(RequestHandler requestHandler, HttpServerEventPublisher eventPublisher, - boolean sendHttp10ResponseFor10Request) { - this.requestHandler = requestHandler; - this.eventPublisher = eventPublisher; - this.sendHttp10ResponseFor10Request = sendHttp10ResponseFor10Request; - } - - @Override - public Observable handle(final Connection, Object> c) { - return c.getInput() - .nest() - .concatMap(new Func1>, Observable>() { - @Override - public Observable call(Observable> reqSource) { - return reqSource.take(1) - .flatMap(new Func1, Observable>() { - @Override - public Observable call(HttpServerRequest req) { - final long startNanos = eventPublisher.publishingEnabled() - ? Clock.newStartTimeNanos() - : -1; - - if (eventPublisher.publishingEnabled()) { - eventPublisher.onNewRequestReceived(); - } - - final HttpServerResponse response = newResponse(req, c); - return handleRequest(req, startNanos, response, c); - } - }); - } - }) - .repeat() - .ambWith(c.closeListener()); - } - - @SuppressWarnings("unchecked") - private Observable handleRequest(HttpServerRequest request, final long startTimeNanos, - final HttpServerResponse response, - final Connection, Object> c) { - Observable requestHandlingResult = null; - try { - - if (request.decoderResult().isSuccess()) { - requestHandlingResult = requestHandler.handle(request, response); - } - - if(null == requestHandlingResult) { - /*If decoding failed an appropriate response status would have been set. - Otherwise, overwrite the status to 500*/ - if (response.getStatus().equals(OK)) { - response.setStatus(INTERNAL_SERVER_ERROR); - } - requestHandlingResult = response.write(Observable.empty()); - } - - } catch (Throwable throwable) { - logger.log(Level.SEVERE, "Unexpected error while invoking HTTP user handler.", throwable); - /*If the headers are already written, then this will produce an error Observable.*/ - requestHandlingResult = response.setStatus(INTERNAL_SERVER_ERROR) - .write(Observable.empty()); - } - - if (eventPublisher.publishingEnabled()) { - requestHandlingResult = requestHandlingResult.lift(new Operator() { - @Override - public Subscriber call(final Subscriber o) { - - if (eventPublisher.publishingEnabled()) { - eventPublisher.onRequestHandlingStart(onEndNanos(startTimeNanos), NANOSECONDS); - } - - return new Subscriber(o) { - @Override - public void onCompleted() { - if (eventPublisher.publishingEnabled()) { - eventPublisher.onRequestHandlingSuccess(onEndNanos(startTimeNanos), - NANOSECONDS); - } - o.onCompleted(); - } - - @Override - public void onError(Throwable e) { - if (eventPublisher.publishingEnabled()) { - eventPublisher.onRequestHandlingFailed(onEndNanos(startTimeNanos), - NANOSECONDS, e); - } - logger.log(Level.SEVERE, "Unexpected error processing a request.", e); - o.onError(e); - } - - @Override - public void onNext(Void aVoid) { - // No Op, its a void - } - }; - } - }); - } - - return requestHandlingResult.onErrorResumeNext(new Func1>() { - @Override - public Observable call(Throwable throwable) { - logger.log(Level.SEVERE, "Unexpected error while processing request.", throwable); - return response.setStatus(INTERNAL_SERVER_ERROR) - .dispose() - .concatWith(c.close()) - .onErrorResumeNext(Observable.empty());// Ignore errors on cleanup - } - }).concatWith(request.dispose()/*Dispose request at the end of processing to discard content if not read*/ - ).concatWith(response.dispose()/*Dispose response at the end of processing to cleanup*/); - - } - - private HttpServerResponse newResponse(HttpServerRequest request, - final Connection, Object> c) { - - /* - * Server should send the highest version it is compatible with. - * http://tools.ietf.org/html/rfc2145#section-2.3 - * - * unless overriden explicitly. - */ - final HttpVersion version = sendHttp10ResponseFor10Request ? request.getHttpVersion() - : HttpVersion.HTTP_1_1; - - HttpResponse responseHeaders; - if (request.decoderResult().isFailure()) { - // As per the spec, we should send 414/431 for URI too long and headers too long, but we do not have - // enough info to decide which kind of failure has caused this error here. - responseHeaders = new DefaultHttpResponse(version, REQUEST_HEADER_FIELDS_TOO_LARGE); - responseHeaders.headers() - .set(CONNECTION, HttpHeaderValues.CLOSE) - .set(CONTENT_LENGTH, 0); - } else { - responseHeaders = new DefaultHttpResponse(version, OK); - } - HttpServerResponse response = HttpServerResponseImpl.create(request, c, responseHeaders); - setConnectionHeader(request, response); - return response; - } - - private void setConnectionHeader(HttpServerRequest request, HttpServerResponse response) { - if (request.isKeepAlive()) { - if (!request.getHttpVersion().isKeepAliveDefault()) { - // Avoid sending keep-alive header if keep alive is default. - // Issue: https://github.com/Netflix/RxNetty/issues/167 - // This optimizes data transferred on the wire. - - // Add keep alive header as per: - // - http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01.html#Connection - response.setHeader(CONNECTION, HttpHeaderValues.KEEP_ALIVE); - } - } else { - response.setHeader(CONNECTION, HttpHeaderValues.CLOSE); - } - } - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServer.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServer.java deleted file mode 100644 index 71e0102..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServer.java +++ /dev/null @@ -1,445 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.ServerChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.ssl.SslHandler; -import io.netty.util.concurrent.EventExecutorGroup; -import io.reactivex.netty.events.EventSource; -import io.reactivex.netty.protocol.http.server.events.HttpServerEventsListener; -import io.reactivex.netty.protocol.tcp.server.TcpServer; -import io.reactivex.netty.ssl.SslCodec; -import rx.functions.Action1; -import rx.functions.Func0; -import rx.functions.Func1; - -import javax.net.ssl.SSLEngine; -import java.net.SocketAddress; -import java.util.concurrent.TimeUnit; - -/** - * An HTTP server. - * - * @param The type of objects received as content from a request to this server. - * @param The type of objects written as content from a response from this server. - */ -public abstract class HttpServer implements EventSource { - - /** - * Creates a new server instance, inheriting all configurations from this server and adding a {@link ChannelOption} - * for the server socket created by the newly created server instance. - * - * @param option Option to add. - * @param value Value for the option. - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer channelOption(ChannelOption option, T value); - - /** - * Creates a new server instance, inheriting all configurations from this server and adding a {@link ChannelOption} - * for the client socket created by the newly created server instance. - * - * @param option Option to add. - * @param value Value for the option. - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer clientChannelOption(ChannelOption option, T value); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this - * server. The specified handler is added at the first position of the pipeline as specified by {@link - * ChannelPipeline#addFirst(String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param name Name of the handler. - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer addChannelHandlerFirst(String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this server. The - * specified handler is added at the first position of the pipeline as specified by {@link - * ChannelPipeline#addFirst(EventExecutorGroup, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param group The {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} methods - * @param name The name of the handler to append - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer addChannelHandlerFirst(EventExecutorGroup group, String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this server. The - * specified handler is added at the last position of the pipeline as specified by - * {@link ChannelPipeline#addLast(String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param name Name of the handler. - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer addChannelHandlerLast(String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this server. The - * specified handler is added at the last position of the pipeline as specified by {@link - * ChannelPipeline#addLast(EventExecutorGroup, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} methods - * @param name the name of the handler to append - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer addChannelHandlerLast(EventExecutorGroup group, String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this server. The - * specified handler is added before an existing handler with the passed {@code baseName} in the pipeline as - * specified by {@link ChannelPipeline#addBefore(String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param baseName the name of the existing handler - * @param name Name of the handler. - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer addChannelHandlerBefore(String baseName, String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this server. The - * specified handler is added before an existing handler with the passed {@code baseName} in the pipeline as - * specified by {@link ChannelPipeline#addBefore(EventExecutorGroup, String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} methods - * @param baseName the name of the existing handler - * @param name the name of the handler to append - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer addChannelHandlerBefore(EventExecutorGroup group, String baseName, - String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this server. The - * specified handler is added after an existing handler with the passed {@code baseName} in the pipeline as - * specified by {@link ChannelPipeline#addAfter(String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param baseName the name of the existing handler - * @param name Name of the handler. - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer addChannelHandlerAfter(String baseName, String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this server. The - * specified handler is added after an existing handler with the passed {@code baseName} in the pipeline as - * specified by {@link ChannelPipeline#addAfter(EventExecutorGroup, String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} methods - * @param baseName the name of the existing handler - * @param name the name of the handler to append - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer addChannelHandlerAfter(EventExecutorGroup group, String baseName, - String name, - Func0 handlerFactory); - - /** - * Creates a new client instances, inheriting all configurations from this client and using the passed action to - * configure all the connections created by the newly created client instance. - * - * @param pipelineConfigurator Action to configure {@link ChannelPipeline}. - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer pipelineConfigurator(Action1 pipelineConfigurator); - - /** - * Creates a new server instances, inheriting all configurations from this server and using the passed - * {@code sslEngineFactory} for all secured connections accepted by the newly created server instance. - * - * If the {@link SSLEngine} instance can be statically, created, {@link #secure(SSLEngine)} can be used. - * - * @param sslEngineFactory Factory for all secured connections created by the newly created server instance. - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer secure(Func1 sslEngineFactory); - - /** - * Creates a new server instances, inheriting all configurations from this server and using the passed - * {@code sslEngine} for all secured connections accepted by the newly created server instance. - * - * If the {@link SSLEngine} instance can not be statically, created, {@link #secure(Func1)} )} can be used. - * - * @param sslEngine {@link SSLEngine} for all secured connections created by the newly created server instance. - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer secure(SSLEngine sslEngine); - - /** - * Creates a new server instances, inheriting all configurations from this server and using the passed - * {@code sslCodec} for all secured connections accepted by the newly created server instance. - * - * This is required only when the {@link SslHandler} used by {@link SslCodec} is to be modified before adding to - * the {@link ChannelPipeline}. For most of the cases, {@link #secure(Func1)} or {@link #secure(SSLEngine)} will be - * enough. - * - * @param sslCodec {@link SslCodec} for all secured connections created by the newly created server instance. - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer secure(SslCodec sslCodec); - - /** - * Creates a new server instances, inheriting all configurations from this server and using a self-signed - * certificate for all secured connections accepted by the newly created server instance. - * - * This is only for testing and should not be used for real production servers. - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer unsafeSecure(); - - /** - * Creates a new client instances, inheriting all configurations from this client and enabling wire logging at the - * passed level for the newly created client instance. - * - * @param wireLoggingLevel Logging level at which the wire logs will be logged. The wire logging will only be done - * if logging is enabled at this level for {@link io.netty.handler.logging.LoggingHandler} - * - * @return A new {@link HttpServer} instance. - * - * @deprecated Use {@link #enableWireLogging(String, LogLevel)} instead. - */ - @Deprecated - public abstract HttpServer enableWireLogging(LogLevel wireLoggingLevel); - - /** - * Creates a new client instances, inheriting all configurations from this client and enabling wire logging at the - * passed level for the newly created client instance. - * - * @param name Name of the logger that can be used to control the logging dynamically. - * @param wireLoggingLevel Logging level at which the wire logs will be logged. The wire logging will only be done - * if logging is enabled at this level for {@link io.netty.handler.logging.LoggingHandler} - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer enableWireLogging(String name, LogLevel wireLoggingLevel); - - /** - * According to the specification, an HTTP server must - * send the highest HTTP version response, it is compatible with. Since, all implementations of this server are - * expected to be HTTP 1.1 compatible, they should always send the response for 1.1. However, in some cases, if - * desired, this behavior can be overridden to send 1.0 response for 1.0 request. If so desired, the behavior can - * be enabled/ disabled by this method. - * - * @param sendHttp10ResponseFor10Request If {@code true} then sends 1.0 version response for 1.0 request. - * - * @return A new {@link HttpServer} instance. - */ - public abstract HttpServer sendHttp10ResponseFor10Request(boolean sendHttp10ResponseFor10Request); - - /** - * Returns the port at which this server is running. - * - * For servers using ephemeral ports, this would return the actual port used, only after the server is started. - * - * @return The port at which this server is running. - */ - public abstract int getServerPort(); - - /** - * Returns the address at which this server is running. - * - * @return The address at which this server is running. - */ - public abstract SocketAddress getServerAddress(); - - /** - * Starts this server. - * - * @param requestHandler Connection handler that will handle any new client connections to this server. - * - * @return This server. - */ - public abstract HttpServer start(RequestHandler requestHandler); - - /** - * Shutdown this server and waits till the server socket is closed. - */ - public abstract void shutdown(); - - /** - * Waits for the shutdown of this server. - * - * This does not actually shutdown the server. It just waits for some other action to shutdown. - */ - public abstract void awaitShutdown(); - - /** - * Waits for the shutdown of this server, waiting a maximum of the passed duration. - * - * This does not actually shutdown the server. It just waits for some other action to shutdown. - * - * @param duration Duration to wait for shutdown. - * @param timeUnit Timeunit for the duration to wait for shutdown. - */ - public abstract void awaitShutdown(long duration, TimeUnit timeUnit); - - /** - * Creates a new server using an ephemeral port. The port used can be found by {@link #getServerPort()} - * - * @return A new {@link HttpServer} - */ - public static HttpServer newServer() { - return _newServer(TcpServer.newServer(0)); - } - - /** - * Creates a new server using the passed port. - * - * @param port Port for the server. {@code 0} to use ephemeral port. - * @return A new {@link HttpServer} - */ - public static HttpServer newServer(int port) { - return _newServer(TcpServer.newServer(port)); - } - - /** - * Creates a new server using the passed port. - * - * @param port Port for the server. {@code 0} to use ephemeral port. - * @param eventLoopGroup Eventloop group to be used for server as well as client sockets. - * @param channelClass The class to be used for server channel. - * - * @return A new {@link HttpServer} - */ - public static HttpServer newServer(int port, EventLoopGroup eventLoopGroup, - Class channelClass) { - return _newServer(TcpServer.newServer(port, eventLoopGroup, eventLoopGroup, channelClass)); - } - - /** - * Creates a new server using the passed port. - * - * @param port Port for the server. {@code 0} to use ephemeral port. - * @param serverGroup Eventloop group to be used for server sockets. - * @param clientGroup Eventloop group to be used for client sockets. - * @param channelClass The class to be used for server channel. - * - * @return A new {@link HttpServer} - */ - public static HttpServer newServer(int port, EventLoopGroup serverGroup, - EventLoopGroup clientGroup, - Class channelClass) { - return _newServer(TcpServer.newServer(port, serverGroup, clientGroup, channelClass)); - } - - /** - * Creates a new server using the passed port. - * - * @param socketAddress Socket address for the server. - * @return A new {@link HttpServer} - */ - public static HttpServer newServer(SocketAddress socketAddress) { - return _newServer(TcpServer.newServer(socketAddress)); - } - - /** - * Creates a new server using the passed port. - * - * @param socketAddress Socket address for the server. - * @param eventLoopGroup Eventloop group to be used for server as well as client sockets. - * @param channelClass The class to be used for server channel. - * - * @return A new {@link HttpServer} - */ - public static HttpServer newServer(SocketAddress socketAddress, EventLoopGroup eventLoopGroup, - Class channelClass) { - return _newServer(TcpServer.newServer(socketAddress, eventLoopGroup, eventLoopGroup, channelClass)); - } - - /** - * Creates a new server using the passed port. - * - * @param socketAddress Socket address for the server. - * @param serverGroup Eventloop group to be used for server sockets. - * @param clientGroup Eventloop group to be used for client sockets. - * @param channelClass The class to be used for server channel. - * - * @return A new {@link HttpServer} - */ - public static HttpServer newServer(SocketAddress socketAddress, EventLoopGroup serverGroup, - EventLoopGroup clientGroup, - Class channelClass) { - return _newServer(TcpServer.newServer(socketAddress, serverGroup, clientGroup, channelClass)); - } - - private static HttpServer _newServer(TcpServer tcpServer) { - return HttpServerImpl.create(tcpServer); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerImpl.java deleted file mode 100644 index ac28e71..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerImpl.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; -import io.netty.handler.logging.LogLevel; -import io.netty.util.concurrent.EventExecutorGroup; -import io.reactivex.netty.protocol.http.HttpHandlerNames; -import io.reactivex.netty.protocol.http.server.events.HttpServerEventPublisher; -import io.reactivex.netty.protocol.http.server.events.HttpServerEventsListener; -import io.reactivex.netty.protocol.http.ws.server.Ws7To13UpgradeHandler; -import io.reactivex.netty.protocol.tcp.server.TcpServer; -import io.reactivex.netty.ssl.SslCodec; -import rx.Subscription; -import rx.functions.Action1; -import rx.functions.Func0; -import rx.functions.Func1; -import rx.subscriptions.CompositeSubscription; - -import javax.net.ssl.SSLEngine; -import java.net.SocketAddress; -import java.util.concurrent.TimeUnit; -import java.util.logging.Logger; - -public final class HttpServerImpl extends HttpServer { - - private final TcpServer, Object> server; - private final HttpServerEventPublisher eventPublisher; - private boolean sendHttp10ResponseFor10Request; - - private HttpServerImpl(TcpServer, Object> server, HttpServerEventPublisher eventPublisher) { - this.server = server; - this.eventPublisher = eventPublisher; - } - - @Override - public HttpServer channelOption(ChannelOption option, T value) { - return _copy(server.channelOption(option, value), eventPublisher); - } - - @Override - public HttpServer clientChannelOption(ChannelOption option, T value) { - return _copy(server.clientChannelOption(option, value), eventPublisher); - } - - @Override - public HttpServer addChannelHandlerFirst(String name, Func0 handlerFactory) { - return _copy(HttpServerImpl.castServer(server.addChannelHandlerFirst(name, handlerFactory)), - eventPublisher); - } - - @Override - public HttpServer addChannelHandlerFirst(EventExecutorGroup group, String name, - Func0 handlerFactory) { - return _copy(HttpServerImpl.castServer(server.addChannelHandlerFirst(group, name, handlerFactory)), - eventPublisher); - } - - @Override - public HttpServer addChannelHandlerLast(String name, Func0 handlerFactory) { - return _copy(HttpServerImpl.castServer(server.addChannelHandlerLast(name, handlerFactory)), - eventPublisher); - } - - @Override - public HttpServer addChannelHandlerLast(EventExecutorGroup group, String name, - Func0 handlerFactory) { - return _copy(HttpServerImpl.castServer(server.addChannelHandlerLast(group, name, handlerFactory)), - eventPublisher); - } - - @Override - public HttpServer addChannelHandlerBefore(String baseName, String name, - Func0 handlerFactory) { - return _copy(HttpServerImpl.castServer(server.addChannelHandlerBefore(baseName, name, handlerFactory)), - eventPublisher); - } - - @Override - public HttpServer addChannelHandlerBefore(EventExecutorGroup group, String baseName, String name, - Func0 handlerFactory) { - return _copy(HttpServerImpl.castServer(server.addChannelHandlerBefore(group, baseName, name, - handlerFactory)), - eventPublisher); - } - - @Override - public HttpServer addChannelHandlerAfter(String baseName, String name, - Func0 handlerFactory) { - return _copy(HttpServerImpl.castServer(server.addChannelHandlerAfter(baseName, name, handlerFactory)), - eventPublisher); - } - - @Override - public HttpServer addChannelHandlerAfter(EventExecutorGroup group, String baseName, String name, - Func0 handlerFactory) { - return _copy(HttpServerImpl.castServer(server.addChannelHandlerAfter(group, baseName, name, - handlerFactory)), - eventPublisher); - } - - @Override - public HttpServer pipelineConfigurator(Action1 pipelineConfigurator) { - return _copy(HttpServerImpl.castServer(server.pipelineConfigurator(pipelineConfigurator)), - eventPublisher); - } - - @Override - public HttpServer secure(Func1 sslEngineFactory) { - return _copy(server.secure(sslEngineFactory), eventPublisher); - } - - @Override - public HttpServer secure(SSLEngine sslEngine) { - return _copy(server.secure(sslEngine), eventPublisher); - } - - @Override - public HttpServer secure(SslCodec sslCodec) { - return _copy(server.secure(sslCodec), eventPublisher); - } - - @Override - public HttpServer unsafeSecure() { - return _copy(server.unsafeSecure(), eventPublisher); - } - - @Override - @Deprecated - public HttpServer enableWireLogging(LogLevel wireLoggingLevel) { - return _copy(server.enableWireLogging(wireLoggingLevel), eventPublisher); - } - - @Override - public HttpServer enableWireLogging(String name, LogLevel wireLoggingLevel) { - return _copy(server.enableWireLogging(name, wireLoggingLevel), eventPublisher); - } - - @Override - public HttpServer sendHttp10ResponseFor10Request(boolean sendHttp10ResponseFor10Request) { - HttpServerImpl toReturn = _copy(server, eventPublisher); - toReturn.sendHttp10ResponseFor10Request = sendHttp10ResponseFor10Request; - return toReturn; - } - - @Override - public int getServerPort() { - return server.getServerPort(); - } - - @Override - public SocketAddress getServerAddress() { - return server.getServerAddress(); - } - - @Override - public HttpServer start(RequestHandler requestHandler) { - server.start(new HttpConnectionHandler<>(requestHandler, eventPublisher, sendHttp10ResponseFor10Request)); - return this; - } - - @Override - public void shutdown() { - server.shutdown(); - } - - @Override - public void awaitShutdown() { - server.awaitShutdown(); - } - - @Override - public void awaitShutdown(long duration, TimeUnit timeUnit) { - server.awaitShutdown(duration, timeUnit); - } - - static HttpServer create(final TcpServer tcpServer) { - final HttpServerEventPublisher eventPublisher = new HttpServerEventPublisher(tcpServer.getEventPublisher()); - return new HttpServerImpl<>( - tcpServer., Object>pipelineConfigurator(new Action1() { - @Override - public void call(ChannelPipeline pipeline) { - pipeline.addLast(HttpHandlerNames.HttpServerEncoder.getName(), new HttpResponseEncoder()); - pipeline.addLast(HttpHandlerNames.HttpServerDecoder.getName(), new HttpRequestDecoder()); - pipeline.addLast(HttpHandlerNames.WsServerUpgradeHandler.getName(), new Ws7To13UpgradeHandler()); - pipeline.addLast(new HttpServerToConnectionBridge<>(eventPublisher)); - } - }), eventPublisher); - } - - @SuppressWarnings("unchecked") - private static TcpServer, Object> castServer(TcpServer rawTypes) { - return (TcpServer, Object>)rawTypes; - } - - private static HttpServerImpl _copy(TcpServer, Object> newServer, - HttpServerEventPublisher oldEventPublisher) { - return new HttpServerImpl<>(newServer, oldEventPublisher.copy(newServer.getEventPublisher())); - } - - @Override - public Subscription subscribe(HttpServerEventsListener listener) { - final CompositeSubscription cs = new CompositeSubscription(); - cs.add(server.subscribe(listener)); - cs.add(eventPublisher.subscribe(listener)); - return cs; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerInterceptorChain.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerInterceptorChain.java deleted file mode 100644 index 3181011..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerInterceptorChain.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.buffer.ByteBuf; -import rx.annotations.Beta; - -/** - * A utility to create an interceptor chain to be used with a {@link HttpServer} to modify behavior of requests - * processed by that server. - * - *

What are interceptors?

- * - * Interceptors can be used to achieve use-cases that involve instrumentation related to any behavior of the requests, - * they can even be used to short-circuit the rest of the chain or to provide canned responses. In order to achieve such - * widely different use-cases, an interceptor is modelled as a simple function that takes one {@link RequestHandler} - * and returns another {@link RequestHandler} instance. With this low level abstraction, any use-case pertaining to - * connection instrumentation can be achieved. - * - * An interceptor chain always starts with an interceptor and ends with a {@link RequestHandler} and any number of - * other interceptors can exist between the start and end.

- * - * A chain can be created by using the various {@code start*()} methods available in this class, eg: - * {@link #start()}, {@link #startRaw()}.

- * - * After starting a chain, any number of other interceptors can be added by using the various {@code next*()} methods - * available in this class, eg: {@link #next(Interceptor)}, {@link #nextWithTransform(TransformingInterceptor)}, - * {@link #nextWithRequestTransform(TransformingInterceptor)} and - * {@link #nextWithResponseTransform(TransformingInterceptor)}

- * - * After adding the required interceptors, by providing a {@link RequestHandler} via the - * {@link #end(RequestHandler)} method, the chain can be ended and the returned {@link RequestHandler} can be used - * with any {@link HttpServer}

- * - * So, a typical interaction with this class would look like:

- * - * {@code - * HttpServer.newServer().start(HttpServerInterceptorChain.start(first).next(second).next(third).end(handler)) - * } - * - *

Simple Interceptor

- * - * For interceptors that do not change the types of objects read or written to the underlying request/response, the - * interface {@link Interceptor} defines the interceptor contract. - * - *

Modifying the type of data read/written to the request/response

- * - * Sometimes, it is required to change the type of objects read from a request or written to a response handled by - * a {@link HttpServer}. For such cases, the interface {@link TransformingInterceptor} - * defines the interceptor contract. Since, this included 4 generic arguments to the interceptor, this is not the base - * type for all interceptors and should be used only when the types of the request/response are actually to be - * changed. - * - * - * The above diagram depicts the execution order of interceptors. The first request handler (internal) is created by - * this class and as is returned by {@link #end(RequestHandler)} method by providing a {@link RequestHandler} that - * does the actual processing of the connection. {@link HttpServer} with which this interceptor chain is used, will - * invoke the internal request handler provided by this class.

- * - * The interceptors are invoked in the order that they are added to this chain. - * - * @param The type of objects received as content from a request to this server. - * @param The type of objects written as content from a response from this server. - * @param The type of objects received as content from a request to this server after applying these interceptors. - * @param The type of objects written as content from a response from this server after applying these - * interceptors. - */ -@Beta -public final class HttpServerInterceptorChain { - - private final TransformingInterceptor interceptor; - - private HttpServerInterceptorChain(TransformingInterceptor interceptor) { - this.interceptor = interceptor; - } - - /** - * Add the next interceptor to this chain. - * - * @param next Next interceptor to add. - * - * @return A new interceptor chain with the interceptors currently existing and the passed interceptor added to the - * end. - */ - public HttpServerInterceptorChain next(final Interceptor next) { - return new HttpServerInterceptorChain<>(new TransformingInterceptor() { - @Override - public RequestHandler intercept(RequestHandler handler) { - return interceptor.intercept(next.intercept(handler)); - } - }); - } - - /** - * Add the next interceptor to this chain, which changes the type of objects read from the request accepted by - * the associated {@link HttpServer}. - * - * @param next Next interceptor to add. - * - * @return A new interceptor chain with the interceptors currently existing and the passed interceptor added to the - * end. - */ - public HttpServerInterceptorChain nextWithRequestTransform(final TransformingInterceptor next) { - return new HttpServerInterceptorChain<>(new TransformingInterceptor() { - @Override - public RequestHandler intercept(RequestHandler handler) { - return interceptor.intercept(next.intercept(handler)); - } - }); - } - - /** - * Add the next interceptor to this chain, which changes the type of objects written to the response sent by - * the associated {@link HttpServer}. - * - * @param next Next interceptor to add. - * - * @return A new interceptor chain with the interceptors current existing and the passed interceptor added to the - * end. - */ - public HttpServerInterceptorChain nextWithResponseTransform(final TransformingInterceptor next) { - return new HttpServerInterceptorChain<>(new TransformingInterceptor() { - @Override - public RequestHandler intercept(RequestHandler handler) { - return interceptor.intercept(next.intercept(handler)); - } - }); - } - - /** - * Add the next interceptor to this chain, which changes the type of objects read read from the request and written - * to the response sent by the associated {@link HttpServer}. - * - * @param next Next interceptor to add. - * - * @return A new interceptor chain with the interceptors current existing and the passed interceptor added to the - * end. - */ - public HttpServerInterceptorChain nextWithTransform(final TransformingInterceptor next) { - return new HttpServerInterceptorChain<>(new TransformingInterceptor() { - @Override - public RequestHandler intercept(RequestHandler handler) { - return interceptor.intercept(next.intercept(handler)); - } - }); - } - - /** - * Terminates this chain with the passed {@link RequestHandler} and returns a {@link RequestHandler} to be - * used by a {@link HttpServer} - * - * @param handler Request handler to use. - * - * @return A request handler that wires the interceptor chain, to be used with {@link HttpServer} instead of - * directly using the passed {@code handler} - */ - public RequestHandler end(RequestHandler handler) { - return interceptor.intercept(handler); - } - - /** - * Starts a new interceptor chain. - * - * @return A new interceptor chain. - */ - public static HttpServerInterceptorChain start() { - return new HttpServerInterceptorChain<>(new TransformingInterceptor() { - @Override - public RequestHandler intercept(RequestHandler handler) { - return handler; - } - }); - } - - /** - * Starts a new interceptor chain with {@link ByteBuf} read and written from request and to responses. - * - * @return A new interceptor chain. - */ - public static HttpServerInterceptorChain startRaw() { - return new HttpServerInterceptorChain<>(new TransformingInterceptor() { - @Override - public RequestHandler intercept(RequestHandler handler) { - return handler; - } - }); - } - - /** - * An interceptor that preserves the type of content of request and response. - * - * @param The type of objects received as content from a request to this server. - * @param The type of objects written as content from a response from this server. - */ - public interface Interceptor { - - /** - * Intercepts and optionally changes the passed {@code RequestHandler}. - * - * @param handler Handler to intercept. - * - * @return Handler to use after this transformation. - */ - RequestHandler intercept(RequestHandler handler); - - } - - /** - * An interceptor that changes the type of content of request and response. - * - * @param The type of objects received as content from a request to this server. - * @param The type of objects written as content from a response from this server. - * @param The type of objects received as content from a request to this server after applying this - * interceptor. - * @param The type of objects written as content from a response from this server after applying this - * interceptor. - */ - public interface TransformingInterceptor { - - /** - * Intercepts and changes the passed {@code RequestHandler}. - * - * @param handler Handler to intercept. - * - * @return Handler to use after this transformation. - */ - RequestHandler intercept(RequestHandler handler); - - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerRequest.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerRequest.java deleted file mode 100644 index b550f49..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerRequest.java +++ /dev/null @@ -1,441 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.cookie.Cookie; -import io.reactivex.netty.channel.ContentSource; -import io.reactivex.netty.protocol.http.internal.HttpMessageFormatter; -import rx.Observable; -import rx.Observable.Transformer; -import rx.Subscriber; - -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -/** - * HTTP server request - * - *

Thread safety

- * - * This object is not thread-safe and must not be used by multiple threads. - * - *

Mutability

- * - * Headers and trailing headers can be mutated for this response. - */ -public abstract class HttpServerRequest { - - /** - * Returns the HTTP method for this request. - * - * @return The HTTP method for this request. - */ - public abstract HttpMethod getHttpMethod(); - - /** - * Returns the HTTP version for this request. - * - * @return The HTTP version for this request. - */ - public abstract HttpVersion getHttpVersion(); - - /** - * Returns the raw URI for the request, including path and query parameters. The URI is not decoded. - * - * @return The raw URI for the request. - */ - public abstract String getUri(); - - /** - * Returns the decoded URI path for this request. - * - * @return The decoded URI path for this request. - */ - public abstract String getDecodedPath(); - - /** - * Returns the query string for this request. The query string is not decoded. - * - * @return The query string for this request. - */ - public abstract String getRawQueryString(); - - /** - * Returns an immutable map of cookie names and cookies contained in this request. - * - * @return An immutable map of cookie names and cookies contained in this request. - */ - public abstract Map> getCookies(); - - /** - * Returns an immutable map of query parameter names and values contained in this request. The names and values for - * the query parameters will be decoded. - * - * @return An immutable map of query parameter names and values contained in this request. - */ - public abstract Map> getQueryParameters(); - - /** - * Checks if there is a header with the passed name in this request. - * - * @param name Name of the header. - * - * @return {@code true} if there is a header with the passed name in this request. - */ - public abstract boolean containsHeader(CharSequence name); - - /** - * Checks if there is a header with the passed name and value in this request. - * - * @param name Name of the header. - * @param value Value of the header. - * @param ignoreCaseValue {@code true} then the value comparision is done ignoring case. - * - * @return {@code true} if there is a header with the passed name and value in this request. - */ - public abstract boolean containsHeader(CharSequence name, CharSequence value, boolean ignoreCaseValue); - - /** - * Returns an iterator over the header entries. Multiple values for the same header appear as separate entries in - * the returned iterator. - * - * @return An iterator over the header entries - */ - public abstract Iterator> headerIterator(); - - /** - * Returns the value of a header with the specified name. If there are more than one values for the specified name, - * the first value is returned. - * - * @param name The name of the header to search - * @return The first header value or {@code null} if there is no such header - */ - public abstract String getHeader(CharSequence name); - - /** - * Returns the value of a header with the specified name. If there are more than one values for the specified name, - * the first value is returned. - * - * @param name The name of the header to search - * @param defaultValue Default if the header does not exist. - * - * @return The first header value or {@code defaultValue} if there is no such header - */ - public abstract String getHeader(CharSequence name, String defaultValue); - - /** - * Returns the values of headers with the specified name - * - * @param name The name of the headers to search - * - * @return A {@link List} of header values which will be empty if no values are found - */ - public abstract List getAllHeaderValues(CharSequence name); - - /** - * Returns the length of the content. - * - * @return the content length - * - * @throws NumberFormatException if the message does not have the {@code "Content-Length"} header or its value is - * not a number. - */ - public abstract long getContentLength(); - - /** - * Returns the length of the content. - * - * @param defaultValue Default value if the message does not have a {@code "Content-Length"} header or its value is - * not a number - * - * @return the content length or {@code defaultValue} if this message does not have the {@code "Content-Length"} - * header or its value is not a number - */ - public abstract long getContentLength(long defaultValue); - - /** - * Returns the date header value with the specified header name. If there are more than one header value for the - * specified header name, the first value is returned. - * The value is parsed as per the - * HTTP specifications using the format: - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name The name of the header to search - * - * @return the header value - */ - public abstract long getDateHeader(CharSequence name); - - /** - * Returns the date header value with the specified header name. If there are more than one header value for the - * specified header name, the first value is returned. - * The value is parsed as per the - * HTTP specifications using the format: - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name The name of the header to search - * @param defaultValue Default value if there is no header with this name. - * - * @return the header value or {@code defaultValue} if there is no header with this name. - */ - public abstract long getDateHeader(CharSequence name, long defaultValue); - - /** - * Returns the value of the {@code "Host"} header. - */ - public abstract String getHostHeader(); - - /** - * Returns the value of the {@code "Host"} header. - * - * @param defaultValue Default if the header does not exist. - * - * @return The value of the {@code "Host"} header or {@code defaultValue} if there is no such header. - */ - public abstract String getHostHeader(String defaultValue); - - /** - * Returns the integer header value with the specified header name. If there are more than one header value for - * the specified header name, the first value is returned. - * - * @param name The name of the header to search - * - * @return the header value - * - * @throws NumberFormatException if there is no such header or the header value is not a number - */ - public abstract int getIntHeader(CharSequence name); - - /** - * Returns the integer header value with the specified header name. If there are more than one header value for - * the specified header name, the first value is returned. - * - * @param name The name of the header to search - * @param defaultValue Default if the header does not exist. - * - * @return the header value or the {@code defaultValue} if there is no such header or the header value is not a - * number - */ - public abstract int getIntHeader(CharSequence name, int defaultValue); - - /** - * Returns {@code true} if and only if this request contains the {@code "Expect: 100-continue"} header. - */ - public abstract boolean is100ContinueExpected(); - - /** - * Returns {@code true} if and only if this request has the content-length header set. - */ - public abstract boolean isContentLengthSet(); - - /** - * Returns {@code true} if and only if the connection can remain open and thus 'kept alive'. This methods respects - * the value of the {@code "Connection"} header first and then the return value of - * {@link HttpVersion#isKeepAliveDefault()}. - */ - public abstract boolean isKeepAlive(); - - /** - * Checks to see if the transfer encoding of this request is chunked - * - * @return True if transfer encoding is chunked, otherwise false - */ - public abstract boolean isTransferEncodingChunked(); - - /** - * Returns a new {@link Set} that contains the names of all headers in this request. Note that modifying the - * returned {@link Set} will not affect the state of this request. - */ - public abstract Set getHeaderNames(); - - /** - * Adds an HTTP header with the passed {@code name} and {@code value} to this request. - * - * @param name Name of the header. - * @param value Value for the header. - * - * @return {@code this} - */ - public abstract HttpServerRequest addHeader(CharSequence name, Object value); - - /** - * Adds the passed {@code cookie} to this request. - * - * @param cookie Cookie to add. - * - * @return {@code this} - */ - public abstract HttpServerRequest addCookie(Cookie cookie); - - /** - * Adds the passed header as a date value to this request. The date is formatted using netty's - * {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the date as per the - * HTTP specifications into the format: - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name Name of the header. - * @param value Value of the header. - * - * @return {@code this} - */ - public abstract HttpServerRequest addDateHeader(CharSequence name, Date value); - - /** - * Adds multiple date values for the passed header name to this request. The date values are formatted using netty's - * {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the date as per the - * HTTP specifications into the format: - * - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name Name of the header. - * @param values Values for the header. - * - * @return {@code this} - */ - public abstract HttpServerRequest addDateHeader(CharSequence name, Iterable values); - - /** - * Adds an HTTP header with the passed {@code name} and {@code values} to this request. - * - * @param name Name of the header. - * @param values Values for the header. - * - * @return {@code this} - */ - public abstract HttpServerRequest addHeader(CharSequence name, Iterable values); - - /** - * Overwrites the current value, if any, of the passed header to the passed date value for this request. The date is - * formatted using netty's {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the date - * as per the HTTP specifications into - * the format: - * - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name Name of the header. - * @param value Value of the header. - * - * @return {@code this} - */ - public abstract HttpServerRequest setDateHeader(CharSequence name, Date value); - - /** - * Overwrites the current value, if any, of the passed header to the passed value for this request. - * - * @param name Name of the header. - * @param value Value of the header. - * - * @return {@code this} - */ - public abstract HttpServerRequest setHeader(CharSequence name, Object value); - - /** - * Overwrites the current value, if any, of the passed header to the passed date values for this request. The date - * is formatted using netty's {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the - * date as per the HTTP specifications - * into the format: - * - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name Name of the header. - * @param values Values of the header. - * - * @return {@code this} - */ - public abstract HttpServerRequest setDateHeader(CharSequence name, Iterable values); - - /** - * Overwrites the current value, if any, of the passed header to the passed values for this request. - * - * @param name Name of the header. - * @param values Values of the header. - * - * @return {@code this} - */ - public abstract HttpServerRequest setHeader(CharSequence name, Iterable values); - - /** - * Removes the passed header from this request. - * - * @param name Name of the header. - * - * @return {@code this} - */ - public abstract HttpServerRequest removeHeader(CharSequence name); - - /** - * Returns the content as a stream. There can only be one {@link Subscriber} to the returned {@link Observable}, any - * subsequent subscriptions will get an error. - * - * @return Stream of content. - */ - public abstract ContentSource getContent(); - - /** - * Subscribes to the content and discards. - * - * @return An {@link Observable}, subscription to which will discard the content. This {@code Observable} will - * error/complete when the content errors/completes and unsubscription from here will unsubscribe from the content. - */ - public abstract Observable discardContent(); - - /** - * Disposes this request. If the content is not yet subscribed, will subscribe and discard the same. - * - * @return An {@link Observable}, subscription to which will dispose this request. If the content is not yet - * subscribed then this is the same as {@link #discardContent()}. - */ - public abstract Observable dispose(); - - /** - * Checks to see if upgrade to websocket protocol is requested by this HTTP request. - * - * @return {@code true} if upgrade to websocket is requested. - */ - public abstract boolean isWebSocketUpgradeRequested(); - - /** - * Creates a new {@code HttpServerRequest} instance modifying the content type using the passed {@code transformer}. - * - * @param transformer Transformer to transform the content stream. - * - * @param New type of the content. - * - * @return A new instance of {@link HttpServerRequest} with the transformed content stream. - */ - public abstract HttpServerRequest transformContent(Transformer transformer); - - /** - * Package private method to get the decoder result from netty. - * - * @return Decoder result. - */ - abstract DecoderResult decoderResult(); - - public String toString() { - return HttpMessageFormatter.formatRequest(getHttpVersion(), getHttpMethod(), getUri(), headerIterator()); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerRequestImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerRequestImpl.java deleted file mode 100644 index 0e5ede1..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerRequestImpl.java +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.channel.Channel; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.cookie.ClientCookieEncoder; -import io.netty.handler.codec.http.cookie.Cookie; -import io.netty.util.ReferenceCountUtil; -import io.reactivex.netty.channel.ContentSource; -import io.reactivex.netty.protocol.http.CookiesHolder; -import io.reactivex.netty.protocol.http.internal.HttpContentSubscriberEvent; -import io.reactivex.netty.protocol.http.ws.server.WebSocketHandshaker; -import rx.Observable; -import rx.Observable.Transformer; -import rx.Subscriber; -import rx.functions.Func1; - -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; - -class HttpServerRequestImpl extends HttpServerRequest { - - private final Channel nettyChannel; - private final HttpRequest nettyRequest; - private final CookiesHolder cookiesHolder; - private final UriInfoHolder uriInfoHolder; - private final ContentSource contentSource; - - HttpServerRequestImpl(HttpRequest nettyRequest, Channel nettyChannel) { - this.nettyRequest = nettyRequest; - this.nettyChannel = nettyChannel; - uriInfoHolder = new UriInfoHolder(this.nettyRequest.uri()); - cookiesHolder = CookiesHolder.newServerRequestHolder(nettyRequest.headers()); - contentSource = new ContentSource<>(nettyChannel, new Func1, Object>() { - @Override - public Object call(Subscriber subscriber) { - return new HttpContentSubscriberEvent<>(subscriber); - } - }); - } - - private HttpServerRequestImpl(HttpRequest nettyRequest, Channel nettyChannel, ContentSource contentSource) { - this.nettyRequest = nettyRequest; - this.nettyChannel = nettyChannel; - uriInfoHolder = new UriInfoHolder(this.nettyRequest.uri()); - cookiesHolder = CookiesHolder.newServerRequestHolder(nettyRequest.headers()); - this.contentSource = contentSource; - } - - @Override - public HttpMethod getHttpMethod() { - return nettyRequest.method(); - } - - @Override - public HttpVersion getHttpVersion() { - return nettyRequest.protocolVersion(); - } - - @Override - public String getUri() { - return uriInfoHolder.getRawUriString(); - } - - @Override - public String getDecodedPath() { - return uriInfoHolder.getPath(); - } - - @Override - public String getRawQueryString() { - return uriInfoHolder.getQueryString(); - } - - @Override - public Map> getCookies() { - return cookiesHolder.getAllCookies(); - } - - @Override - public Map> getQueryParameters() { - return uriInfoHolder.getQueryParameters(); - } - - @Override - public boolean containsHeader(CharSequence name) { - return nettyRequest.headers().contains(name); - } - - @Override - public boolean containsHeader(CharSequence name, CharSequence value, boolean ignoreCaseValue) { - return nettyRequest.headers().contains(name, value, ignoreCaseValue); - } - - @Override - public Iterator> headerIterator() { - return nettyRequest.headers().iteratorCharSequence(); - } - - @Override - public String getHeader(CharSequence name) { - return nettyRequest.headers().get(name); - } - - @Override - public String getHeader(CharSequence name, String defaultValue) { - return nettyRequest.headers().get(name, defaultValue); - } - - @Override - public List getAllHeaderValues(CharSequence name) { - return nettyRequest.headers().getAll(name); - } - - @Override - public long getContentLength() { - return HttpUtil.getContentLength(nettyRequest); - } - - @Override - public long getContentLength(long defaultValue) { - return HttpUtil.getContentLength(nettyRequest, defaultValue); - } - - @Override - public long getDateHeader(CharSequence name) { - return nettyRequest.headers().getTimeMillis(name); - } - - @Override - public long getDateHeader(CharSequence name, long defaultValue) { - return nettyRequest.headers().getTimeMillis(name, defaultValue); - } - - @Override - public String getHostHeader() { - return nettyRequest.headers().get(HOST); - } - - @Override - public String getHostHeader(String defaultValue) { - return nettyRequest.headers().get(HOST, defaultValue); - } - - @Override - public int getIntHeader(CharSequence name) { - return nettyRequest.headers().getInt(name); - } - - @Override - public int getIntHeader(CharSequence name, int defaultValue) { - return nettyRequest.headers().getInt(name, defaultValue); - } - - @Override - public boolean is100ContinueExpected() { - return HttpUtil.is100ContinueExpected(nettyRequest); - } - - @Override - public boolean isContentLengthSet() { - return HttpUtil.isContentLengthSet(nettyRequest); - } - - @Override - public boolean isKeepAlive() { - return HttpUtil.isKeepAlive(nettyRequest); - } - - @Override - public boolean isTransferEncodingChunked() { - return HttpUtil.isTransferEncodingChunked(nettyRequest); - } - - @Override - public Set getHeaderNames() { - return nettyRequest.headers().names(); - } - - @Override - public HttpServerRequest addHeader(CharSequence name, Object value) { - nettyRequest.headers().add(name, value); - return this; - } - - @Override - public HttpServerRequest addCookie(Cookie cookie) { - nettyRequest.headers().add(COOKIE, - ClientCookieEncoder.STRICT.encode(cookie) /*Since this is a request object, cookies are - as if coming from a client*/); - return this; - - } - - @Override - public HttpServerRequest addDateHeader(CharSequence name, Date value) { - nettyRequest.headers().add(name, value); - return this; - } - - @Override - public HttpServerRequest addDateHeader(CharSequence name, Iterable values) { - for (Date value : values) { - nettyRequest.headers().add(name, value); - } - return this; - } - - @Override - public HttpServerRequest addHeader(CharSequence name, Iterable values) { - nettyRequest.headers().add(name, values); - return this; - } - - @Override - public HttpServerRequest setDateHeader(CharSequence name, Date value) { - nettyRequest.headers().set(name, value); - return this; - } - - @Override - public HttpServerRequest setHeader(CharSequence name, Object value) { - nettyRequest.headers().set(name, value); - return this; - } - - @Override - public HttpServerRequest setDateHeader(CharSequence name, Iterable values) { - for (Date value : values) { - nettyRequest.headers().set(name, value); - } - return this; - } - - @Override - public HttpServerRequest setHeader(CharSequence name, Iterable values) { - nettyRequest.headers().add(name, values); - return this; - } - - @Override - public HttpServerRequest removeHeader(CharSequence name) { - nettyRequest.headers().remove(name); - return this; - } - - @Override - public ContentSource getContent() { - return contentSource; - } - - @Override - public Observable discardContent() { - return getContent().map(new Func1() { - @Override - public Void call(T t) { - ReferenceCountUtil.release(t); - return null; - } - }).ignoreElements(); - } - - @Override - public Observable dispose() { - return discardContent().onErrorResumeNext(Observable.empty()); - } - - @Override - public boolean isWebSocketUpgradeRequested() { - return WebSocketHandshaker.isUpgradeRequested(this); - } - - @Override - public HttpServerRequest transformContent(Transformer transformer) { - return new HttpServerRequestImpl<>(nettyRequest, nettyChannel, contentSource.transform(transformer)); - } - - @Override - DecoderResult decoderResult() { - return nettyRequest.decoderResult(); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerResponse.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerResponse.java deleted file mode 100644 index 93577f7..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerResponse.java +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.channel.Channel; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.cookie.Cookie; -import io.reactivex.netty.channel.AllocatingTransformer; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.protocol.http.sse.ServerSentEvent; -import io.reactivex.netty.protocol.http.ws.server.WebSocketHandler; -import io.reactivex.netty.protocol.http.ws.server.WebSocketHandshaker; -import rx.Observable; -import rx.annotations.Experimental; - -import java.text.ParseException; -import java.util.Date; -import java.util.List; -import java.util.Set; - -/** - * An HTTP server response. - * - *

Thread safety

- * - * This object is not thread safe and should not be accessed from multiple threads. - * - * @param The type of objects written as the content of the response. - */ -public abstract class HttpServerResponse extends ResponseContentWriter { - - protected HttpServerResponse(OnSubscribe f) { - super(f); - } - - /** - * Returns the status of this response. If the status is not explicitly set, the default value is - * {@link HttpResponseStatus#OK} - * - * @return The status of this response. - */ - public abstract HttpResponseStatus getStatus(); - - /** - * Checks if there is a header with the passed name in this response. - * - * @param name Name of the header. - * - * @return {@code true} if there is a header with the passed name in this response. - */ - public abstract boolean containsHeader(CharSequence name); - - /** - * Checks if there is a header with the passed name and value in this response. - * - * @param name Name of the header. - * @param value Value of the header. - * @param ignoreCaseValue {@code true} then the value comparision is done ignoring case. - * - * @return {@code true} if there is a header with the passed name and value in this response. - */ - public abstract boolean containsHeader(CharSequence name, CharSequence value, boolean ignoreCaseValue); - - /** - * Returns the value of a header with the specified name. If there are more than one values for the specified name, - * the first value is returned. - * - * @param name The name of the header to search - * @return The first header value or {@code null} if there is no such header - */ - public abstract String getHeader(CharSequence name); - - /** - * Returns the value of a header with the specified name. If there are more than one values for the specified name, - * the first value is returned. - * - * @param name The name of the header to search - * @param defaultValue Default if the header does not exist. - * - * @return The first header value or {@code defaultValue} if there is no such header - */ - public abstract String getHeader(CharSequence name, String defaultValue); - - /** - * Returns the values of headers with the specified name - * - * @param name The name of the headers to search - * - * @return A {@link List} of header values which will be empty if no values are found - */ - public abstract List getAllHeaderValues(CharSequence name); - - /** - * Returns the date header value with the specified header name. If there are more than one header value for the - * specified header name, the first value is returned. - * The value is parsed as per the - * HTTP specifications using the format: - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name The name of the header to search - * - * @return the header value - * - * @throws ParseException if there is no such header or the header value is not a formatted date - */ - public abstract long getDateHeader(CharSequence name) throws ParseException; - - /** - * Returns the date header value with the specified header name. If there are more than one header value for the - * specified header name, the first value is returned. - * The value is parsed as per the - * HTTP specifications using the format: - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name The name of the header to search - * @param defaultValue Default value if there is no header with this name. - * - * @return the header value or {@code defaultValue} if there is no header with this name. - */ - public abstract long getDateHeader(CharSequence name, long defaultValue); - - /** - * Returns the integer header value with the specified header name. If there are more than one header value for - * the specified header name, the first value is returned. - * - * @param name The name of the header to search - * - * @return the header value - * - * @throws NumberFormatException if there is no such header or the header value is not a number - */ - public abstract int getIntHeader(CharSequence name); - - /** - * Returns the integer header value with the specified header name. If there are more than one header value for - * the specified header name, the first value is returned. - * - * @param name The name of the header to search - * @param defaultValue Default if the header does not exist. - * - * @return the header value or the {@code defaultValue} if there is no such header or the header value is not a - * number - */ - public abstract int getIntHeader(CharSequence name, int defaultValue); - - /** - * Returns a new {@link Set} that contains the names of all headers in this response. Note that modifying the - * returned {@link Set} will not affect the state of this response. - */ - public abstract Set getHeaderNames(); - - /** - * Adds an HTTP header with the passed {@code name} and {@code value} to this response. - * - * @param name Name of the header. - * @param value Value for the header. - * - * @return {@code this} - */ - public abstract HttpServerResponse addHeader(CharSequence name, Object value); - - /** - * Adds the passed {@code cookie} to this response. - * - * @param cookie Cookie to add. - * - * @return {@code this} - */ - public abstract HttpServerResponse addCookie(Cookie cookie); - - /** - * Adds the passed header as a date value to this response. The date is formatted using netty's - * {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the date as per the - * HTTP specifications into the format: - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name Name of the header. - * @param value Value of the header. - * - * @return {@code this} - */ - public abstract HttpServerResponse addDateHeader(CharSequence name, Date value); - - /** - * Adds multiple date values for the passed header name to this response. The date values are formatted using netty's - * {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the date as per the - * HTTP specifications into the format: - * - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name Name of the header. - * @param values Values for the header. - * - * @return {@code this} - */ - public abstract HttpServerResponse addDateHeader(CharSequence name, Iterable values); - - /** - * Adds an HTTP header with the passed {@code name} and {@code values} to this response. - * - * @param name Name of the header. - * @param values Values for the header. - * - * @return {@code this} - */ - public abstract HttpServerResponse addHeader(CharSequence name, Iterable values); - - /** - * Overwrites the current value, if any, of the passed header to the passed date value for this response. The date is - * formatted using netty's {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the date - * as per the HTTP specifications into - * the format: - * - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name Name of the header. - * @param value Value of the header. - * - * @return {@code this} - */ - public abstract HttpServerResponse setDateHeader(CharSequence name, Date value); - - /** - * Overwrites the current value, if any, of the passed header to the passed value for this response. - * - * @param name Name of the header. - * @param value Value of the header. - * - * @return {@code this} - */ - public abstract HttpServerResponse setHeader(CharSequence name, Object value); - - /** - * Overwrites the current value, if any, of the passed header to the passed date values for this response. The date - * is formatted using netty's {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the - * date as per the HTTP specifications - * into the format: - * - *
"E, dd MMM yyyy HH:mm:ss z"
- * - * @param name Name of the header. - * @param values Values of the header. - * - * @return {@code this} - */ - public abstract HttpServerResponse setDateHeader(CharSequence name, Iterable values); - - /** - * Overwrites the current value, if any, of the passed header to the passed values for this response. - * - * @param name Name of the header. - * @param values Values of the header. - * - * @return {@code this} - */ - public abstract HttpServerResponse setHeader(CharSequence name, Iterable values); - - /** - * Removes the passed header from this response. - * - * @param name Name of the header. - * - * @return {@code this} - */ - public abstract HttpServerResponse removeHeader(CharSequence name); - - /** - * Sets the status for the response. - * - * @param status Status to set. - * - * @return {@code this} - */ - public abstract HttpServerResponse setStatus(HttpResponseStatus status); - - /** - * Sets the HTTP transfer encoding to chunked for this response. This delegates to - * {@link HttpHeaders#setTransferEncodingChunked(HttpMessage)} - * - * @return {@code this} - */ - public abstract HttpServerResponse setTransferEncodingChunked(); - - /** - * This is a performance optimization to not flush the channel on every response send. - * - * This can be used - * only when the processing for a server is not asynchronous, in which case, one would have to flush the responses - * written explicitly (done on completion of the {@link Observable} written). Something like this: - * -
-     resp.sendHeaders()
-         .writeStringAndFlushOnEach(Observable.interval(1, TimeUnit.SECONDS))
-                                              .map(aLong -$gt; "Interval =>" + aLong)
-                                   )
-     
- * - * This can be used when the response is written synchronously from a {@link RequestHandler}, something like: - * -
-     response.writeString(Observable.just("Hello world");
-     
- * - * When set, this will make the channel to be flushed only when all the requests available on the channel are - * read. Thus, making it possible to do a gathering write for all pipelined requests on a connection. This reduces - * the number of system calls and is helpful in "Hello World" benchmarks. - */ - public abstract HttpServerResponse flushOnlyOnReadComplete(); - - /** - * Sends the headers for this response when the returned {@code Observable} is subscribed. Alternatively, one can - * continue to write contents using the returned {@link ResponseContentWriter} - * - * @return {@link ResponseContentWriter} which can be subscribed to only send the headers or to write payload. - */ - public abstract ResponseContentWriter sendHeaders(); - - /** - * Converts this response to enable writing {@link ServerSentEvent}s. - * - * @return This response with writing of {@link ServerSentEvent} enabled. - */ - @Experimental - public abstract HttpServerResponse transformToServerSentEvents(); - - /** - * Creates a new {@code HttpServerResponse} instance modifying the content type using the passed {@code transformer}. - * - * @param transformer Transformer to transform the content stream. - * - * @param New type of the content. - * - * @return A new instance of {@link HttpServerResponse} with the transformed content stream. - */ - public abstract HttpServerResponse transformContent(AllocatingTransformer transformer); - - /** - * Accepts the upgrade to websockets, if requested and after sending a successful handshake response, - * invokes the passed handler to handle the websocket connection. - * - * If any changes to this response are required for the handshake, they should be done before invoking this method. - * - * @return {@link WebSocketHandshaker} for sending a handshake to the client. Subscription to the handshaker, will - * send the handshake. - */ - public abstract WebSocketHandshaker acceptWebSocketUpgrade(WebSocketHandler handler); - - /** - * Disposes this response. If the response is not yet set then this will attempt to send an error response if the - * connection is still open. - * - * @return An {@link Observable}, subscription to which will dispose this response. - */ - public abstract Observable dispose(); - - /** - * Returns the underlying channel on which this response was received. - * - * @return The underlying channel on which this response was received. - */ - public abstract Channel unsafeNettyChannel(); - - /** - * Returns the underlying connection on which this response was received. - * - * @return The underlying connection on which this response was received. - */ - public abstract Connection unsafeConnection(); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerResponseImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerResponseImpl.java deleted file mode 100644 index 050cf46..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerResponseImpl.java +++ /dev/null @@ -1,432 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.channel.Channel; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.cookie.Cookie; -import io.netty.handler.codec.http.cookie.ServerCookieEncoder; -import io.reactivex.netty.channel.AllocatingTransformer; -import io.reactivex.netty.channel.ChannelOperations; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.MarkAwarePipeline; -import io.reactivex.netty.protocol.http.HttpHandlerNames; -import io.reactivex.netty.protocol.http.TrailingHeaders; -import io.reactivex.netty.protocol.http.sse.ServerSentEvent; -import io.reactivex.netty.protocol.http.sse.server.ServerSentEventEncoder; -import io.reactivex.netty.protocol.http.ws.server.WebSocketHandler; -import io.reactivex.netty.protocol.http.ws.server.WebSocketHandshaker; -import rx.Observable; -import rx.Subscriber; -import rx.functions.Action0; -import rx.functions.Func0; -import rx.functions.Func1; -import rx.functions.Func2; - -import java.util.Date; -import java.util.List; -import java.util.Set; - -public final class HttpServerResponseImpl extends HttpServerResponse { - - private final State state; - - private HttpServerResponseImpl(final State state) { - super(new OnSubscribe() { - @Override - public void call(Subscriber subscriber) { - state.sendHeaders().unsafeSubscribe(subscriber); - } - }); - this.state = state; - } - - @Override - public HttpResponseStatus getStatus() { - return state.headers.status(); - } - - @Override - public boolean containsHeader(CharSequence name) { - return state.headers.headers().contains(name); - } - - @Override - public boolean containsHeader(CharSequence name, CharSequence value, boolean ignoreCaseValue) { - return state.headers.headers().contains(name, value, ignoreCaseValue); - } - - @Override - public String getHeader(CharSequence name) { - return state.headers.headers().get(name); - } - - @Override - public String getHeader(CharSequence name, String defaultValue) { - return state.headers.headers().get(name, defaultValue); - } - - @Override - public List getAllHeaderValues(CharSequence name) { - return state.headers.headers().getAll(name); - } - - @Override - public long getDateHeader(CharSequence name) { - return state.headers.headers().getTimeMillis(name); - } - - @Override - public long getDateHeader(CharSequence name, long defaultValue) { - return state.headers.headers().getTimeMillis(name, defaultValue); - } - - @Override - public int getIntHeader(CharSequence name) { - return state.headers.headers().getInt(name); - } - - @Override - public int getIntHeader(CharSequence name, int defaultValue) { - return state.headers.headers().getInt(name, defaultValue); - } - - @Override - public Set getHeaderNames() { - return state.headers.headers().names(); - } - - @Override - public HttpServerResponse addHeader(CharSequence name, Object value) { - if (state.allowUpdate()) { - state.headers.headers().add(name, value); - } - return this; - } - - @Override - public HttpServerResponse addCookie(Cookie cookie) { - if (state.allowUpdate()) { - state.headers.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode(cookie)); - } - return this; - } - - @Override - public HttpServerResponse addDateHeader(CharSequence name, Date value) { - if (state.allowUpdate()) { - state.headers.headers().add(name, value); - } - return this; - } - - @Override - public HttpServerResponse addDateHeader(CharSequence name, Iterable values) { - if (state.allowUpdate()) { - for (Date value : values) { - state.headers.headers().add(name, value); - } - } - return this; - } - - @Override - public HttpServerResponse addHeader(CharSequence name, Iterable values) { - if (state.allowUpdate()) { - state.headers.headers().add(name, values); - } - return this; - } - - @Override - public HttpServerResponse setDateHeader(CharSequence name, Date value) { - if (state.allowUpdate()) { - state.headers.headers().set(name, value); - } - return this; - } - - @Override - public HttpServerResponse setHeader(CharSequence name, Object value) { - if (state.allowUpdate()) { - state.headers.headers().set(name, value); - } - return this; - } - - @Override - public HttpServerResponse setDateHeader(CharSequence name, Iterable values) { - if (state.allowUpdate()) { - for (Date value : values) { - state.headers.headers().set(name, value); - } - } - return this; - } - - @Override - public HttpServerResponse setHeader(CharSequence name, Iterable values) { - if (state.allowUpdate()) { - state.headers.headers().set(name, values); - } - return this; - } - - @Override - public HttpServerResponse removeHeader(CharSequence name) { - if (state.allowUpdate()) { - state.headers.headers().remove(name); - } - return this; - } - - @Override - public HttpServerResponse setStatus(HttpResponseStatus status) { - if (state.allowUpdate()) { - state.headers.setStatus(status); - } - return this; - } - - @Override - public HttpServerResponse setTransferEncodingChunked() { - if (state.allowUpdate()) { - HttpUtil.setTransferEncodingChunked(state.headers, true); - } - return this; - } - - @Override - public HttpServerResponse flushOnlyOnReadComplete() { - // Does not need to be guarded by allowUpdate() as flush semantics can be changed anytime. - state.connection.unsafeNettyChannel().attr(ChannelOperations.FLUSH_ONLY_ON_READ_COMPLETE).set(true); - return this; - } - - @Override - public ResponseContentWriter sendHeaders() { - return state.sendHeaders(); - } - - @Override - public HttpServerResponse transformToServerSentEvents() { - markAwarePipeline().addAfter(HttpHandlerNames.HttpServerEncoder.getName(), - HttpHandlerNames.SseServerCodec.getName(), - new ServerSentEventEncoder()); - return _cast(); - } - - @Override - public HttpServerResponse transformContent(AllocatingTransformer transformer) { - @SuppressWarnings("unchecked") - Connection transformedC = state.connection.transformWrite(transformer); - return new HttpServerResponseImpl<>(new State(state, transformedC)); - } - - @Override - public WebSocketHandshaker acceptWebSocketUpgrade(WebSocketHandler handler) { - return WebSocketHandshaker.isUpgradeRequested(state.request) - ? WebSocketHandshaker.newHandshaker(state.request, this, handler) - : WebSocketHandshaker.newErrorHandshaker(new IllegalStateException("WebSocket upgrade was not requested.")); - } - - @Override - public Observable dispose() { - return Observable.defer(new Func0>() { - @Override - public Observable call() { - return (state.allowUpdate() ? write(Observable.empty()) : Observable.empty()) - .doOnSubscribe(new Action0() { - @Override - public void call() { - state.connection - .getResettableChannelPipeline() - .reset(); - } - }); - } - }); - } - - @Override - public Channel unsafeNettyChannel() { - return state.connection.unsafeNettyChannel(); - } - - @Override - public Connection unsafeConnection() { - return state.connection; - } - - @Override - public ResponseContentWriter write(Observable msgs) { - return state.sendHeaders().write(msgs); - } - - @Override - public Observable write(Observable contentSource, Func0 trailerFactory, - Func2 trailerMutator) { - return state.sendHeaders().write(contentSource, trailerFactory, trailerMutator); - } - - @Override - public Observable write(Observable contentSource, Func0 trailerFactory, - Func2 trailerMutator, - Func1 flushSelector) { - return state.sendHeaders().write(contentSource, trailerFactory, trailerMutator, flushSelector); - } - - @Override - public ResponseContentWriter write(Observable msgs, Func1 flushSelector) { - return state.sendHeaders().write(msgs, flushSelector); - } - - @Override - public ResponseContentWriter writeAndFlushOnEach(Observable msgs) { - return state.sendHeaders().writeAndFlushOnEach(msgs); - } - - @Override - public ResponseContentWriter writeString(Observable msgs) { - return state.sendHeaders().writeString(msgs); - } - - @Override - public Observable writeString(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator) { - return state.sendHeaders().writeString(contentSource, trailerFactory, trailerMutator); - } - - @Override - public Observable writeString(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator, - Func1 flushSelector) { - return state.sendHeaders().writeString(contentSource, trailerFactory, trailerMutator, flushSelector); - } - - @Override - public ResponseContentWriter writeString(Observable msgs, Func1 flushSelector) { - return state.sendHeaders().writeString(msgs, flushSelector); - } - - @Override - public ResponseContentWriter writeStringAndFlushOnEach(Observable msgs) { - return state.sendHeaders().writeStringAndFlushOnEach(msgs); - } - - @Override - public ResponseContentWriter writeBytes(Observable msgs) { - return state.sendHeaders().writeBytes(msgs); - } - - @Override - public Observable writeBytes(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator) { - return state.sendHeaders().writeBytes(contentSource, trailerFactory, trailerMutator); - } - - @Override - public Observable writeBytes(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator, - Func1 flushSelector) { - return state.sendHeaders().writeBytes(contentSource, trailerFactory, trailerMutator, flushSelector); - } - - @Override - public ResponseContentWriter writeBytes(Observable msgs, Func1 flushSelector) { - return state.sendHeaders().writeBytes(msgs, flushSelector); - } - - @Override - public ResponseContentWriter writeBytesAndFlushOnEach(Observable msgs) { - return state.sendHeaders().writeBytesAndFlushOnEach(msgs); - } - - public static HttpServerResponse create(HttpServerRequest request, - @SuppressWarnings("rawtypes") Connection connection, - HttpResponse headers) { - final State newState = new State<>(headers, connection, request); - return new HttpServerResponseImpl<>(newState); - } - - @SuppressWarnings("unchecked") - private HttpServerResponse _cast() { - return (HttpServerResponse) this; - } - - private MarkAwarePipeline markAwarePipeline() { - return state.connection.getResettableChannelPipeline().markIfNotYetMarked(); - } - - private static class State { - - private final HttpResponse headers; - - @SuppressWarnings("rawtypes") - private final Connection connection; - private final HttpServerRequest request; - /*This links the headers sent dynamic state from one response to a child response - (created via a mutation method). If it is a simple boolean, then a copy of state will just lead to a copy by - value and not reference.*/ - private final HeaderSentStateHolder sentStateHolder; - - private State(HttpResponse headers, @SuppressWarnings("rawtypes") Connection connection, HttpServerRequest request) { - this.headers = headers; - this.connection = connection; - this.request = request; - this.sentStateHolder = new HeaderSentStateHolder(); - } - - public State(State state, Connection connection) { - this.headers = state.headers; - this.request = state.request; - this.sentStateHolder = state.sentStateHolder; - this.connection = connection; - } - - private boolean allowUpdate() { - return !sentStateHolder.headersSent; - } - - public ResponseContentWriter sendHeaders() { - if (allowUpdate()) { - sentStateHolder.headersSent = true; - return new ContentWriterImpl<>(connection, headers); - } - - return new FailedContentWriter<>(); - } - - } - - private static final class HeaderSentStateHolder implements Func0 { - - private boolean headersSent = false; - - @Override - public Object call() { - return headersSent; - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerToConnectionBridge.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerToConnectionBridge.java deleted file mode 100644 index 0ba820a..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/HttpServerToConnectionBridge.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.LastHttpContent; -import io.reactivex.netty.channel.ChannelOperations; -import io.reactivex.netty.events.Clock; -import io.reactivex.netty.protocol.http.internal.AbstractHttpConnectionBridge; -import io.reactivex.netty.protocol.http.internal.HttpContentSubscriberEvent; -import io.reactivex.netty.protocol.http.server.events.HttpServerEventPublisher; -import rx.functions.Action0; -import rx.subscriptions.Subscriptions; - -import java.util.ArrayDeque; -import java.util.Queue; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static java.util.concurrent.TimeUnit.*; - -public class HttpServerToConnectionBridge extends AbstractHttpConnectionBridge { - - private static final Logger logger = Logger.getLogger(HttpServerToConnectionBridge.class.getName()); - - private volatile boolean activeContentSubscriberExists; - - private final Object contentSubGuard = new Object(); - private Queue> pendingContentSubs; /*Guarded by contentSubGuard*/ - private final HttpServerEventPublisher eventPublisher; - private int lastSeenResponseCode; - - public HttpServerToConnectionBridge(HttpServerEventPublisher eventPublisher) { - this.eventPublisher = eventPublisher; - } - - @Override - protected void beforeOutboundHeaderWrite(HttpMessage httpMsg, ChannelPromise promise, final long startTimeNanos) { - HttpResponse response = (HttpResponse) httpMsg; - if (eventPublisher.publishingEnabled()) { - eventPublisher.onResponseWriteStart(); - } - lastSeenResponseCode = response.status().code(); - } - - @Override - protected void onOutboundLastContentWrite(LastHttpContent msg, ChannelPromise promise, - final long headerWriteStartTimeNanos) { - final int _responseCode = lastSeenResponseCode; - - if (eventPublisher.publishingEnabled()) { - promise.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (eventPublisher.publishingEnabled()) { - long endNanos = Clock.onEndNanos(headerWriteStartTimeNanos); - if (future.isSuccess()) { - eventPublisher.onResponseWriteSuccess(endNanos, NANOSECONDS, _responseCode); - } else { - eventPublisher.onResponseWriteFailed(endNanos, NANOSECONDS, future.cause()); - } - } - } - }); - } - } - - @Override - public void userEventTriggered(final ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof HttpContentSubscriberEvent) { - - final HttpContentSubscriberEvent subscriberEvent = (HttpContentSubscriberEvent) evt; - subscriberEvent.getSubscriber().add(Subscriptions.create(new Action0() { - @Override - public void call() { - HttpContentSubscriberEvent nextSub = null; - synchronized (contentSubGuard) { - if (null != pendingContentSubs) { - nextSub = pendingContentSubs.poll(); - } - } - - activeContentSubscriberExists = null != nextSub; - if (null != nextSub) { - fireContentSubscriberEvent(ctx, nextSub); - } - } - })); - - if (activeContentSubscriberExists) { - synchronized (contentSubGuard) { - if (null == pendingContentSubs) { - pendingContentSubs = new ArrayDeque<>(); /*Guarded by contentSubGuard*/ - } - pendingContentSubs.add(subscriberEvent); - } - return; - } - - activeContentSubscriberExists = true; - } - - // TODO: Handle trailers - super.userEventTriggered(ctx, evt); - } - - @Override - protected boolean isInboundHeader(Object nextItem) { - return nextItem instanceof HttpRequest; - } - - @Override - protected boolean isOutboundHeader(Object nextItem) { - return nextItem instanceof HttpResponse; - } - - @Override - protected Object newHttpObject(Object nextItem, Channel channel) { - if (eventPublisher.publishingEnabled()) { - eventPublisher.onRequestHeadersReceived(); - } - return new HttpServerRequestImpl<>((HttpRequest) nextItem, channel); - } - - @Override - protected void onContentReceived() { - if (eventPublisher.publishingEnabled()) { - eventPublisher.onRequestContentReceived(); - } - } - - @Override - protected void onContentReceiveComplete(long receiveStartTimeNanos) { - if (eventPublisher.publishingEnabled()) { - eventPublisher.onRequestReceiveComplete(Clock.onEndNanos(receiveStartTimeNanos), NANOSECONDS); - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - super.channelReadComplete(ctx); - Boolean shouldFlush = ctx.channel().attr(ChannelOperations.FLUSH_ONLY_ON_READ_COMPLETE).get(); - if (null != shouldFlush && shouldFlush) { - ctx.flush(); /*This is a no-op if there is nothing to flush but supports HttpServerResponse.flushOnlyOnReadComplete()*/ - } - } - - private void fireContentSubscriberEvent(ChannelHandlerContext ctx, HttpContentSubscriberEvent event) { - try { - super.userEventTriggered(ctx, event); - } catch (Exception e) { - try { - exceptionCaught(ctx, e); - } catch (Exception e1) { - logger.log(Level.SEVERE, "Exception while handling error in handler.", e1); - } - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/RequestHandler.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/RequestHandler.java deleted file mode 100644 index 1404d5e..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/RequestHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import rx.Observable; - -/** - * A handler for an {@link HttpServerRequest} to produce a {@link HttpServerResponse} - * - * @param The type of objects received as content from the request. - * @param The type of objects written as content from the response. - */ -public interface RequestHandler { - - /** - * Provides a request and response pair to process. - * - * @param request Http request to process. - * @param response Http response to populate after processing the request. - * - * @return An {@link Observable} that represents the processing of the request. Subscribing to this should start - * the request processing and unsubscribing should cancel the processing. - */ - Observable handle(HttpServerRequest request, HttpServerResponse response); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/ResponseContentWriter.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/ResponseContentWriter.java deleted file mode 100644 index 82cdba6..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/ResponseContentWriter.java +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.reactivex.netty.protocol.http.TrailingHeaders; -import rx.Observable; -import rx.functions.Func0; -import rx.functions.Func1; -import rx.functions.Func2; - -/** - * A facility to optionally write content to the response. - * - *

Thread safety

- * - * This object is not thread-safe and can not be accessed from multiple threads. - */ -public abstract class ResponseContentWriter extends Observable { - - ResponseContentWriter(OnSubscribe f) { - super(f); - } - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel. - * - * The writes are flushed when the passed stream completes. - * - * @param msgs Stream of messages to write. - * - * @return {@link Observable} representing the result of this write. Every subscription to this {@link Observable} - * will replay the write on the channel. - */ - public abstract ResponseContentWriter write(Observable msgs); - - /** - * Uses the passed {@link Observable} as the source of content for this request. This method provides a way to - * write trailing headers. - * - * A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed - * {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify - * the trailing headers instance. - * - * This method can not be invoked multiple times for the same response as on completion of the passed - * source, it writes the trailing headers and trailing headers can only be written once for an HTTP response. - * So, any subsequent invocation of this method will always emit an error when subscribed. - * - * The writes are flushed when the passed stream completes. - * - * @param contentSource Content source for the response. - * @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content. - * @param trailerMutator A function to mutate the trailing header on each item emitted from the content source. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - public abstract Observable write(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator); - - /** - * Uses the passed {@link Observable} as the source of content for this request. This method provides a way to - * write trailing headers. - * - * A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed - * {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify - * the trailing headers instance. - * - * This method can not be invoked multiple times for the same response as on completion of the passed - * source, it writes the trailing headers and trailing headers can only be written once for an HTTP response. - * So, any subsequent invocation of this method will always emit an error when subscribed. - * - * @param contentSource Content source for the response. - * @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content. - * @param trailerMutator A function to mutate the trailing header on each item emitted from the content source. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. Channel is - * flushed, iff this function returns, {@code true}. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - public abstract Observable write(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator, - Func1 flushSelector); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel - * and flushes the channel, everytime, {@code flushSelector} returns {@code true} . Any writes issued before - * subscribing, will also be flushed. However, the returned {@link Observable} will not capture the result of those - * writes, i.e. if the other writes, fail and this write does not, the returned {@link Observable} will not fail. - * - * @param msgs Message stream to write. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. - * Channel is flushed, iff this function returns, {@code true}. - * - * @return An {@link Observable} representing the result of this and all writes done prior to the flush. Every - * subscription to this {@link Observable} will write the passed messages and flush all pending writes, when the - * {@code flushSelector} returns {@code true} - */ - public abstract ResponseContentWriter write(Observable msgs, Func1 flushSelector); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel - * and flushes the channel, on every write. Any writes issued before subscribing, will also be flushed. However, the - * returned {@link Observable} will not capture the result of those writes, i.e. if the other writes, fail and this - * write does not, the returned {@link Observable} will not fail. - * - * @param msgs Message stream to write. - * - * @return An {@link Observable} representing the result of this and all writes done prior to the flush. Every - * subscription to this {@link Observable} will write the passed messages and flush all pending writes, on every - * write. - */ - public abstract ResponseContentWriter writeAndFlushOnEach(Observable msgs); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel. - * - * The writes are flushed when the passed stream completes. - * - * @param msgs Stream of messages to write. - * - * @return {@link Observable} representing the result of this write. Every subscription to this {@link Observable} - * will replay the write on the channel. - */ - public abstract ResponseContentWriter writeString(Observable msgs); - - /** - * Uses the passed {@link Observable} as the source of content for this request. This method provides a way to - * write trailing headers. - * - * A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed - * {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify - * the trailing headers instance. - * - * This method can not be invoked multiple times for the same response as on completion of the passed - * source, it writes the trailing headers and trailing headers can only be written once for an HTTP response. - * So, any subsequent invocation of this method will always emit an error when subscribed. - * - * The writes are flushed when the passed stream completes. - * - * @param contentSource Content source for the response. - * @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content. - * @param trailerMutator A function to mutate the trailing header on each item emitted from the content source. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - public abstract Observable writeString(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator); - - /** - * Uses the passed {@link Observable} as the source of content for this request. This method provides a way to - * write trailing headers. - * - * A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed - * {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify - * the trailing headers instance. - * - * This method can not be invoked multiple times for the same response as on completion of the passed - * source, it writes the trailing headers and trailing headers can only be written once for an HTTP response. - * So, any subsequent invocation of this method will always emit an error when subscribed. - * - * @param contentSource Content source for the response. - * @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content. - * @param trailerMutator A function to mutate the trailing header on each item emitted from the content source. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. Channel is - * flushed, iff this function returns, {@code true}. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - public abstract Observable writeString(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator, - Func1 flushSelector); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel - * and flushes the channel, everytime, {@code flushSelector} returns {@code true} . Any writes issued before - * subscribing, will also be flushed. However, the returned {@link Observable} will not capture the result of those - * writes, i.e. if the other writes, fail and this write does not, the returned {@link Observable} will not fail. - * - * @param msgs Message stream to write. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. - * Channel is flushed, iff this function returns, {@code true}. - * - * @return An {@link Observable} representing the result of this and all writes done prior to the flush. Every - * subscription to this {@link Observable} will write the passed messages and flush all pending writes, when the - * {@code flushSelector} returns {@code true} - */ - public abstract ResponseContentWriter writeString(Observable msgs, Func1 flushSelector); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel - * and flushes the channel, on every write. Any writes issued before subscribing, will also be flushed. However, the - * returned {@link Observable} will not capture the result of those writes, i.e. if the other writes, fail and this - * write does not, the returned {@link Observable} will not fail. - * - * @param msgs Message stream to write. - * - * @return An {@link Observable} representing the result of this and all writes done prior to the flush. Every - * subscription to this {@link Observable} will write the passed messages and flush all pending writes, on every - * write. - */ - public abstract ResponseContentWriter writeStringAndFlushOnEach(Observable msgs); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel. - * - * The writes are flushed when the passed stream completes. - * - * @param msgs Stream of messages to write. - * - * @return {@link Observable} representing the result of this write. Every subscription to this {@link Observable} - * will replay the write on the channel. - */ - public abstract ResponseContentWriter writeBytes(Observable msgs); - - /** - * Uses the passed {@link Observable} as the source of content for this request. This method provides a way to - * write trailing headers. - * - * A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed - * {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify - * the trailing headers instance. - * - * This method can not be invoked multiple times for the same response as on completion of the passed - * source, it writes the trailing headers and trailing headers can only be written once for an HTTP response. - * So, any subsequent invocation of this method will always emit an error when subscribed. - * - * The writes are flushed when the passed stream completes. - * - * @param contentSource Content source for the response. - * @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content. - * @param trailerMutator A function to mutate the trailing header on each item emitted from the content source. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - public abstract Observable writeBytes(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator); - - /** - * Uses the passed {@link Observable} as the source of content for this request. This method provides a way to - * write trailing headers. - * - * A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed - * {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify - * the trailing headers instance. - * - * This method can not be invoked multiple times for the same response as on completion of the passed - * source, it writes the trailing headers and trailing headers can only be written once for an HTTP response. - * So, any subsequent invocation of this method will always emit an error when subscribed. - * - * @param contentSource Content source for the response. - * @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content. - * @param trailerMutator A function to mutate the trailing header on each item emitted from the content source. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. Channel is - * flushed, iff this function returns, {@code true}. - * - * @return An new instance of {@link Observable} which can be subscribed to execute the request. - */ - public abstract Observable writeBytes(Observable contentSource, - Func0 trailerFactory, - Func2 trailerMutator, - Func1 flushSelector); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel - * and flushes the channel, everytime, {@code flushSelector} returns {@code true} . Any writes issued before - * subscribing, will also be flushed. However, the returned {@link Observable} will not capture the result of those - * writes, i.e. if the other writes, fail and this write does not, the returned {@link Observable} will not fail. - * - * @param msgs Message stream to write. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. - * Channel is flushed, iff this function returns, {@code true}. - * - * @return An {@link Observable} representing the result of this and all writes done prior to the flush. Every - * subscription to this {@link Observable} will write the passed messages and flush all pending writes, when the - * {@code flushSelector} returns {@code true} - */ - public abstract ResponseContentWriter writeBytes(Observable msgs, Func1 flushSelector); - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel - * and flushes the channel, on every write. Any writes issued before subscribing, will also be flushed. However, the - * returned {@link Observable} will not capture the result of those writes, i.e. if the other writes, fail and this - * write does not, the returned {@link Observable} will not fail. - * - * @param msgs Message stream to write. - * - * @return An {@link Observable} representing the result of this and all writes done prior to the flush. Every - * subscription to this {@link Observable} will write the passed messages and flush all pending writes, on every - * write. - */ - public abstract ResponseContentWriter writeBytesAndFlushOnEach(Observable msgs); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/UriInfoHolder.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/UriInfoHolder.java deleted file mode 100644 index 6ae17d1..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/UriInfoHolder.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.handler.codec.http.QueryStringDecoder; - -import java.util.List; -import java.util.Map; - -public class UriInfoHolder { - - private final String uri; - private final String queryString; - private final QueryStringDecoder decoder; - - public UriInfoHolder(String uri) { - this.uri = uri; - - // java.net.URI doesn't support a relaxed mode and fails for many URIs that get used - // in practice - int indexOfStartOfQP = uri.indexOf('?'); - if (-1 != indexOfStartOfQP && uri.length() >= indexOfStartOfQP) { - queryString = uri.substring(indexOfStartOfQP + 1); - } else { - queryString = ""; - } - - decoder = new QueryStringDecoder(getPath(uri)); - } - - // If it is a relative URI then just pass it to the decoder. Otherwise we need to remove - // everything before the path. This method assumes the first '/' after the scheme is the - // start of the path. - private static String getPath(String uri) { - int offset = 0; - if (uri.startsWith("http://")) { - offset = "http://".length(); - } else if (uri.startsWith("https://")) { - offset = "https://".length(); - } - - if (offset == 0) { - return uri; - } else { - int firstSlash = uri.indexOf('/', offset); - return -1 != firstSlash? uri.substring(firstSlash) : uri; - } - } - - public String getRawUriString() { - return uri; - } - - public synchronized String getPath() { - return decoder.path(); - } - - public String getQueryString() { - return queryString; - } - - public synchronized Map> getQueryParameters() { - return decoder.parameters(); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/events/HttpServerEventPublisher.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/events/HttpServerEventPublisher.java deleted file mode 100644 index 408d0f3..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/events/HttpServerEventPublisher.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server.events; - -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.events.EventSource; -import io.reactivex.netty.events.ListenersHolder; -import io.reactivex.netty.events.internal.SafeEventListener; -import io.reactivex.netty.protocol.tcp.server.events.TcpServerEventListener; -import io.reactivex.netty.protocol.tcp.server.events.TcpServerEventPublisher; -import rx.Subscription; -import rx.functions.Action1; -import rx.functions.Action3; -import rx.functions.Action4; -import rx.subscriptions.CompositeSubscription; - -import java.util.concurrent.TimeUnit; - -public final class HttpServerEventPublisher extends HttpServerEventsListener - implements EventSource, EventPublisher { - - private static final Action1 NEW_REQUEST_ACTION = new Action1() { - @Override - public void call(HttpServerEventsListener l) { - l.onNewRequestReceived(); - } - }; - - private static final Action3 HANDLE_START_ACTION = - new Action3() { - @Override - public void call(HttpServerEventsListener l, Long duration, TimeUnit timeUnit) { - l.onRequestHandlingStart(duration, timeUnit); - } - }; - - private static final Action3 HANDLE_SUCCESS_ACTION = - new Action3() { - @Override - public void call(HttpServerEventsListener l, Long duration, TimeUnit timeUnit) { - l.onRequestHandlingSuccess(duration, timeUnit); - } - }; - - private static final Action4 HANDLE_FAILED_ACTION = - new Action4() { - @Override - public void call(HttpServerEventsListener l, Long duration, TimeUnit timeUnit, Throwable t) { - l.onRequestHandlingFailed(duration, timeUnit, t); - } - }; - - private static final Action1 HEADER_RECIEVED_ACTION = new Action1() { - @Override - public void call(HttpServerEventsListener l) { - l.onRequestHeadersReceived(); - } - }; - - private static final Action1 CONTENT_RECIEVED_ACTION = new Action1() { - @Override - public void call(HttpServerEventsListener l) { - l.onRequestContentReceived(); - } - }; - - private static final Action3 REQ_RECV_COMPLETE_ACTION = - new Action3() { - @Override - public void call(HttpServerEventsListener l, Long duration, TimeUnit timeUnit) { - l.onRequestReceiveComplete(duration, timeUnit); - } - }; - - private static final Action1 RESP_WRITE_START_ACTION = - new Action1() { - @Override - public void call(HttpServerEventsListener l) { - l.onResponseWriteStart(); - } - }; - - private static final Action4 RESP_WRITE_SUCCESS_ACTION = - new Action4() { - @Override - public void call(HttpServerEventsListener l, Long duration, TimeUnit timeUnit, Integer respCode) { - l.onResponseWriteSuccess(duration, timeUnit, respCode); - } - }; - - private static final Action4 RESP_WRITE_FAILED_ACTION = - new Action4() { - @Override - public void call(HttpServerEventsListener l, Long duration, TimeUnit timeUnit, Throwable t) { - l.onResponseWriteFailed(duration, timeUnit, t); - } - }; - - private final ListenersHolder listeners; - private final TcpServerEventPublisher tcpDelegate; - - public HttpServerEventPublisher(TcpServerEventPublisher tcpDelegate) { - listeners = new ListenersHolder<>(); - this.tcpDelegate = tcpDelegate; - } - - public HttpServerEventPublisher(TcpServerEventPublisher tcpDelegate, ListenersHolder l) { - this.tcpDelegate = tcpDelegate; - listeners = l; - } - - @Override - public void onNewRequestReceived() { - listeners.invokeListeners(NEW_REQUEST_ACTION); - } - - @Override - public void onRequestHandlingStart(final long duration, final TimeUnit timeUnit) { - listeners.invokeListeners(HANDLE_START_ACTION, duration, timeUnit); - } - - @Override - public void onRequestHandlingSuccess(final long duration, final TimeUnit timeUnit) { - listeners.invokeListeners(HANDLE_SUCCESS_ACTION, duration, timeUnit); - } - - @Override - public void onRequestHandlingFailed(final long duration, final TimeUnit timeUnit, final Throwable throwable) { - listeners.invokeListeners(HANDLE_FAILED_ACTION, duration, timeUnit, throwable); - } - - @Override - public void onRequestHeadersReceived() { - listeners.invokeListeners(HEADER_RECIEVED_ACTION); - } - - @Override - public void onRequestContentReceived() { - listeners.invokeListeners(CONTENT_RECIEVED_ACTION); - } - - @Override - public void onRequestReceiveComplete(final long duration, final TimeUnit timeUnit) { - listeners.invokeListeners(REQ_RECV_COMPLETE_ACTION, duration, timeUnit); - } - - @Override - public void onResponseWriteStart() { - listeners.invokeListeners(RESP_WRITE_START_ACTION); - } - - @Override - public void onResponseWriteSuccess(final long duration, final TimeUnit timeUnit, final int responseCode) { - listeners.invokeListeners(RESP_WRITE_SUCCESS_ACTION, duration, timeUnit, responseCode); - } - - @Override - public void onResponseWriteFailed(final long duration, final TimeUnit timeUnit, final Throwable throwable) { - listeners.invokeListeners(RESP_WRITE_FAILED_ACTION, duration, timeUnit, throwable); - } - - @Override - public void onConnectionCloseFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - tcpDelegate.onConnectionCloseFailed(duration, timeUnit, throwable); - } - - @Override - public void onConnectionCloseSuccess(long duration, TimeUnit timeUnit) { - tcpDelegate.onConnectionCloseSuccess(duration, timeUnit); - } - - @Override - public void onConnectionCloseStart() { - tcpDelegate.onConnectionCloseStart(); - } - - @Override - public void onWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - tcpDelegate.onWriteFailed(duration, timeUnit, throwable); - } - - @Override - public void onWriteSuccess(long duration, TimeUnit timeUnit) { - tcpDelegate.onWriteSuccess(duration, timeUnit); - } - - @Override - public void onWriteStart() { - tcpDelegate.onWriteStart(); - } - - @Override - public void onFlushComplete(long duration, TimeUnit timeUnit) { - tcpDelegate.onFlushComplete(duration, timeUnit); - } - - @Override - public void onFlushStart() { - tcpDelegate.onFlushStart(); - } - - @Override - public void onByteRead(long bytesRead) { - tcpDelegate.onByteRead(bytesRead); - } - - @Override - public void onByteWritten(long bytesWritten) { - tcpDelegate.onByteWritten(bytesWritten); - } - - @Override - public void onConnectionHandlingFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - tcpDelegate.onConnectionHandlingFailed(duration, timeUnit, throwable); - } - - @Override - public void onConnectionHandlingSuccess(long duration, TimeUnit timeUnit) { - tcpDelegate.onConnectionHandlingSuccess(duration, timeUnit); - } - - @Override - public void onConnectionHandlingStart(long duration, TimeUnit timeUnit) { - tcpDelegate.onConnectionHandlingStart(duration, timeUnit); - } - - @Override - public void onCustomEvent(Object event) { - tcpDelegate.onCustomEvent(event); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { - tcpDelegate.onCustomEvent(event, duration, timeUnit); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { - tcpDelegate.onCustomEvent(event, duration, timeUnit, throwable); - } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { - tcpDelegate.onCustomEvent(event, throwable); - } - - @Override - public void onNewClientConnected() { - tcpDelegate.onNewClientConnected(); - } - - @Override - public boolean publishingEnabled() { - return listeners.publishingEnabled(); - } - - @Override - public Subscription subscribe(HttpServerEventsListener listener) { - if (!SafeEventListener.class.isAssignableFrom(listener.getClass())) { - listener = new SafeHttpServerEventsListener(listener); - } - - CompositeSubscription cs = new CompositeSubscription(); - cs.add(listeners.subscribe(listener)); - - TcpServerEventListener tcpListener = listener; - - if (listener instanceof SafeHttpServerEventsListener) { - tcpListener = ((SafeHttpServerEventsListener) listener).unwrap(); - } - - cs.add(tcpDelegate.subscribe(tcpListener)); - - return cs; - } - - public HttpServerEventPublisher copy(TcpServerEventPublisher newTcpDelegate) { - return new HttpServerEventPublisher(newTcpDelegate, listeners.copy()); - } - - /*Visible for testing*/ListenersHolder getListeners() { - return listeners; - } - - /*Visible for testing*/TcpServerEventPublisher getTcpDelegate() { - return tcpDelegate; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/events/HttpServerEventsListener.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/events/HttpServerEventsListener.java deleted file mode 100644 index 72de587..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/events/HttpServerEventsListener.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server.events; - -import io.reactivex.netty.protocol.http.server.HttpServer; -import io.reactivex.netty.protocol.tcp.server.events.TcpServerEventListener; - -import java.util.concurrent.TimeUnit; - -/** - * A listener for all events published by {@link HttpServer} - */ -public abstract class HttpServerEventsListener extends TcpServerEventListener { - - /** - * Event whenever a new request is received by the server. - */ - public void onNewRequestReceived() {} - - /** - * When request handling started. - * - * @param duration Time between the receiving request and start of processing. - * @param timeUnit Time unit for the duration. - */ - @SuppressWarnings("unused") - public void onRequestHandlingStart(long duration, TimeUnit timeUnit) { } - - /** - * When request handling completes successfully. - * - * @param duration Time between the request processing start and completion. - * @param timeUnit Time unit for the duration. - */ - @SuppressWarnings("unused") - public void onRequestHandlingSuccess(long duration, TimeUnit timeUnit) {} - - /** - * When request handling completes with an error. - * - * @param duration Time between the request processing start and failure. - * @param timeUnit Time unit for the duration. - * @param throwable Error that caused the failure. - */ - @SuppressWarnings("unused") - public void onRequestHandlingFailed(long duration, TimeUnit timeUnit, Throwable throwable) {} - - /** - * Whenever request headers are received. - */ - public void onRequestHeadersReceived() {} - - /** - * Event whenever an HTTP request content is received (an HTTP request can have multiple content chunks, in which - * case this event will be fired as many times for the same request). - */ - public void onRequestContentReceived() {} - - /** - * Event when the request receive is completed. - * - * @param duration Time taken between receiving the request headers and completion of request. - * @param timeUnit Time unit for the duration. - */ - @SuppressWarnings("unused") - public void onRequestReceiveComplete(long duration, TimeUnit timeUnit) {} - - /** - * Event when the response write starts. - */ - @SuppressWarnings("unused") - public void onResponseWriteStart() {} - - /** - * Event when the response write is completed successfully. - * - * @param duration Time taken between write start and completion. - * @param timeUnit Time unit for the duration. - * @param responseCode HTTP response code for the response. - */ - @SuppressWarnings("unused") - public void onResponseWriteSuccess(long duration, TimeUnit timeUnit, int responseCode) {} - - /** - * Event when the response write is completed with an error. - * - * @param duration Time taken between write start and completion. - * @param timeUnit Time unit for the duration. - * @param throwable Error that caused the failure. - */ - @SuppressWarnings("unused") - public void onResponseWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) {} -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/events/SafeHttpServerEventsListener.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/events/SafeHttpServerEventsListener.java deleted file mode 100644 index 7fa50da..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/server/events/SafeHttpServerEventsListener.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server.events; - -import io.reactivex.netty.events.internal.SafeEventListener; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -final class SafeHttpServerEventsListener extends HttpServerEventsListener implements SafeEventListener { - - private final AtomicBoolean completed = new AtomicBoolean(); - private final HttpServerEventsListener delegate; - - public SafeHttpServerEventsListener(HttpServerEventsListener delegate) { - this.delegate = delegate; - } - - @Override - public void onCompleted() { - if (completed.compareAndSet(false, true)) { - delegate.onCompleted(); - } - } - - @Override - public void onNewRequestReceived() { - if (!completed.get()) { - delegate.onNewRequestReceived(); - } - } - - @Override - public void onRequestHandlingStart(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onRequestHandlingStart(duration, timeUnit); - } - } - - @Override - public void onRequestHandlingSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onRequestHandlingSuccess(duration, timeUnit); - } - } - - @Override - public void onRequestHandlingFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - if (!completed.get()) { - delegate.onRequestHandlingFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onRequestHeadersReceived() { - if (!completed.get()) { - delegate.onRequestHeadersReceived(); - } - } - - @Override - public void onRequestContentReceived() { - if (!completed.get()) { - delegate.onRequestContentReceived(); - } - } - - @Override - public void onRequestReceiveComplete(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onRequestReceiveComplete(duration, timeUnit); - } - } - - @Override - public void onResponseWriteStart() { - if (!completed.get()) { - delegate.onResponseWriteStart(); - } - } - - @Override - public void onResponseWriteSuccess(long duration, TimeUnit timeUnit, int responseCode) { - if (!completed.get()) { - delegate.onResponseWriteSuccess(duration, timeUnit, responseCode); - } - } - - @Override - public void onResponseWriteFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - if (!completed.get()) { - delegate.onResponseWriteFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onNewClientConnected() { - if (!completed.get()) { - delegate.onNewClientConnected(); - } - } - - @Override - public void onConnectionHandlingStart(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onConnectionHandlingStart(duration, timeUnit); - } - } - - @Override - public void onConnectionHandlingSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onConnectionHandlingSuccess(duration, timeUnit); - } - } - - @Override - public void onConnectionHandlingFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - if (!completed.get()) { - delegate.onConnectionHandlingFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onByteRead(long bytesRead) { - if (!completed.get()) { - delegate.onByteRead(bytesRead); - } - } - - @Override - public void onByteWritten(long bytesWritten) { - if (!completed.get()) { - delegate.onByteWritten(bytesWritten); - } - } - - @Override - public void onFlushStart() { - if (!completed.get()) { - delegate.onFlushStart(); - } - } - - @Override - public void onFlushComplete(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onFlushComplete(duration, timeUnit); - } - } - - @Override - public void onWriteStart() { - if (!completed.get()) { - delegate.onWriteStart(); - } - } - - @Override - public void onWriteSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onWriteSuccess(duration, timeUnit); - } - } - - @Override - public void onWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - if (!completed.get()) { - delegate.onWriteFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onConnectionCloseStart() { - if (!completed.get()) { - delegate.onConnectionCloseStart(); - } - } - - @Override - public void onConnectionCloseSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onConnectionCloseSuccess(duration, timeUnit); - } - } - - @Override - public void onConnectionCloseFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - if (!completed.get()) { - delegate.onConnectionCloseFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onCustomEvent(Object event) { - if (!completed.get()) { - delegate.onCustomEvent(event); - } - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onCustomEvent(event, duration, timeUnit); - } - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { - if (!completed.get()) { - delegate.onCustomEvent(event, duration, timeUnit, throwable); - } - } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { - if (!completed.get()) { - delegate.onCustomEvent(event, throwable); - } - } - - public HttpServerEventsListener unwrap() { - return delegate; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof SafeHttpServerEventsListener)) { - return false; - } - - SafeHttpServerEventsListener that = (SafeHttpServerEventsListener) o; - - return !(delegate != null? !delegate.equals(that.delegate) : that.delegate != null); - - } - - @Override - public int hashCode() { - return delegate != null? delegate.hashCode() : 0; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/sse/ServerSentEvent.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/sse/ServerSentEvent.java deleted file mode 100644 index 84879d9..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/sse/ServerSentEvent.java +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.sse; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.buffer.Unpooled; - -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * An object representing a server-sent-event following the SSE specifications - * - * A server sent event is composed of the following: - * - *
    -
  • Event id: This is the last event id seen on the stream this event was received. This can be null, if no id is received.
  • -
  • Event type: The last seen event type seen on the stream this event was received. This can be null, if no type is received.
  • -
  • Data: This is the actual event data.
  • -
- * - *

Type

- * - * A {@link ServerSentEvent} is of the type {@link Type#Data} unless it is explicitly passed on creation. - * - *

Memory management

- * - * This is an implementation of {@link ByteBufHolder} so it is required to be explicitly released by calling - * {@link #release()} when this instance is no longer required. - */ -public class ServerSentEvent implements ByteBufHolder { - - private static final Logger logger = Logger.getLogger(ServerSentEvent.class.getName()); - - private static Charset sseEncodingCharset; - - static { - try { - sseEncodingCharset = StandardCharsets.UTF_8; - } catch (Exception e) { - logger.log(Level.SEVERE, "UTF-8 charset not available. Since SSE only contains UTF-8 data, we can not read SSE data."); - sseEncodingCharset = null; - } - } - - public enum Type { - Data, - Id, - EventType - } - - private final Type type; - /*This is required to make sure we allocate ByteBuf only inside an eventloop, else the ByteBuf pool will grow in the - * owner thread*/ - private final String dataAsString; - private final ByteBuf data; - private final ByteBuf eventId; - private final ByteBuf eventType; - - public ServerSentEvent(Type type, ByteBuf data) { - this(type, null, null, data); - } - - public ServerSentEvent(ByteBuf data) { - this(Type.Data, data); - } - - public ServerSentEvent(ByteBuf eventId, ByteBuf eventType, ByteBuf data) { - this(Type.Data, eventId, eventType, data); - } - - protected ServerSentEvent(Type type, ByteBuf eventId, ByteBuf eventType, ByteBuf data) { - dataAsString = null; - this.data = data; - this.type = type; - this.eventId = eventId; - this.eventType = eventType; - } - - private ServerSentEvent(String data) { - dataAsString = data; - this.data = null; - type = Type.Data; - eventId = null; - eventType = null; - } - - /** - * The type of this event. For events which contain an event Id or event type along with data, the type is still - * {@link Type#Data}. The type will be {@link Type#Id} or {@link Type#EventType} only if the event just contains the - * event type or event id and no data. - * - * @return Type of this event. - */ - public Type getType() { - return type; - } - - public boolean hasEventId() { - return null != eventId; - } - - public boolean hasEventType() { - return null != eventType; - } - - public ByteBuf getEventId() { - return eventId; - } - - public String getEventIdAsString() { - return eventId.toString(getSseCharset()); - } - - public ByteBuf getEventType() { - return eventType; - } - - public String getEventTypeAsString() { - return eventType.toString(getSseCharset()); - } - - public boolean hasDataAsString() { - return null != dataAsString; - } - - public String contentAsString() { - return null != dataAsString ? dataAsString : data.toString(getSseCharset()); - } - - @Override - public ByteBuf content() { - return null != data ? data : Unpooled.buffer().writeBytes(dataAsString.getBytes(getSseCharset())); - } - - @Override - public ByteBufHolder copy() { - if (hasDataAsString()) { - return new ServerSentEvent(dataAsString); - } else { - return new ServerSentEvent(type, null != eventId? eventId.copy() : null, - null != eventType? eventType.copy() : null, data.copy()); - } - } - - @Override - public ByteBufHolder duplicate() { - if (hasDataAsString()) { - return new ServerSentEvent(dataAsString); - } else { - return new ServerSentEvent(type, null != eventId ? eventId.duplicate() : null, - null != eventType ? eventType.duplicate() : null, data.duplicate()); - } - } - - @Override - public ByteBufHolder retainedDuplicate() { - return duplicate().retain(); - } - - @Override - public ByteBufHolder replace(ByteBuf content) { - return new ServerSentEvent(content); - } - - @Override - public int refCnt() { - return hasDataAsString() ? 1 : data.refCnt(); // Ref count is consistent across data, eventId and eventType - } - - @Override - public ByteBufHolder retain() { - if(hasEventId()) { - eventId.retain(); - } - if(hasEventType()) { - eventType.retain(); - } - if (!hasDataAsString()) { - data.retain(); - } - return this; - } - - @Override - public ByteBufHolder retain(int increment) { - if(hasEventId()) { - eventId.retain(increment); - } - if(hasEventType()) { - eventType.retain(increment); - } - if (!hasDataAsString()) { - data.retain(increment); - } - return this; - } - - @Override - public ByteBufHolder touch() { - return touch(null); - } - - @Override - public ByteBufHolder touch(Object hint) { - if (!hasDataAsString()) { - data.touch(hint); - } - return this; - } - - @Override - public boolean release() { - return data.release(1); - } - - @Override - public boolean release(int decrement) { - if(hasEventId()) { - eventId.release(decrement); - } - if(hasEventType()) { - eventType.release(decrement); - } - return data.release(decrement); - } - - /** - * Creates a {@link ServerSentEvent} instance with an event id. - * - * @param eventId Id for the event. - * @param data Data for the event. - * - * @return The {@link ServerSentEvent} instance. - */ - public static ServerSentEvent withEventId(ByteBuf eventId, ByteBuf data) { - return new ServerSentEvent(eventId, null, data); - } - - /** - * Creates a {@link ServerSentEvent} instance with an event type. - * - * @param eventType Type for the event. - * @param data Data for the event. - * - * @return The {@link ServerSentEvent} instance. - */ - public static ServerSentEvent withEventType(ByteBuf eventType, ByteBuf data) { - return new ServerSentEvent(null, eventType, data); - } - - /** - * Creates a {@link ServerSentEvent} instance with an event id and type. - * - * @param eventType Type for the event. - * @param eventId Id for the event. - * @param data Data for the event. - * - * @return The {@link ServerSentEvent} instance. - */ - public static ServerSentEvent withEventIdAndType(ByteBuf eventId, ByteBuf eventType, ByteBuf data) { - return new ServerSentEvent(eventId, eventType, data); - } - - /** - * Creates a {@link ServerSentEvent} instance with data. - * - * @param data Data for the event. - * - * @return The {@link ServerSentEvent} instance. - */ - public static ServerSentEvent withData(ByteBuf data) { - return new ServerSentEvent(data); - } - - /** - * Creates a {@link ServerSentEvent} instance with data. - * - * @param data Data for the event. - * - * @return The {@link ServerSentEvent} instance. - */ - public static ServerSentEvent withData(String data) { - return new ServerSentEvent(data); - } - - protected Charset getSseCharset() { - return null == sseEncodingCharset ? Charset.forName("UTF-8") : sseEncodingCharset; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder(); - if (hasEventId()) { - sb.append("id: "); - sb.append(getEventIdAsString()); - sb.append('\n'); - } - - if (hasEventType()) { - sb.append("event: "); - sb.append(getEventTypeAsString()); - sb.append('\n'); - } - - sb.append("data: "); - sb.append(contentAsString()); - sb.append('\n'); - - return sb.toString(); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/sse/client/ServerSentEventDecoder.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/sse/client/ServerSentEventDecoder.java deleted file mode 100644 index dd8ba8b..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/sse/client/ServerSentEventDecoder.java +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.sse.client; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageDecoder; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.util.ByteProcessor; -import io.reactivex.netty.protocol.http.sse.ServerSentEvent; -import io.reactivex.netty.protocol.http.sse.ServerSentEvent.Type; - -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A decoder to decode Server sent events into {@link ServerSentEvent} - */ -public class ServerSentEventDecoder extends MessageToMessageDecoder { - - private static final Logger logger = Logger.getLogger(ServerSentEventDecoder.class.getName()); - - public static final int DEFAULT_MAX_FIELD_LENGTH = 100; - - private static final char[] EVENT_ID_FIELD_NAME = "event".toCharArray(); - private static final char[] DATA_FIELD_NAME = "data".toCharArray(); - private static final char[] ID_FIELD_NAME = "id".toCharArray(); - - protected static final ByteProcessor SKIP_TILL_LINE_DELIMITER_PROCESSOR = new ByteProcessor() { - @Override - public boolean process(byte value) throws Exception { - return !isLineDelimiter((char) value); - } - }; - - protected static final ByteProcessor SKIP_LINE_DELIMITERS_AND_SPACES_PROCESSOR = new ByteProcessor() { - @Override - public boolean process(byte value) throws Exception { - return isLineDelimiter((char) value) || (char) value == ' '; - } - }; - - protected static final ByteProcessor SKIP_COLON_AND_WHITE_SPACE_PROCESSOR = new ByteProcessor() { - @Override - public boolean process(byte value) throws Exception { - char valueChar = (char) value; - return valueChar == ':' || valueChar == ' '; - } - }; - - protected static final ByteProcessor SCAN_COLON_PROCESSOR = new ByteProcessor() { - @Override - public boolean process(byte value) throws Exception { - return (char) value != ':'; - } - }; - - protected static final ByteProcessor SCAN_EOL_PROCESSOR = value -> !isLineDelimiter((char) value); - - private static Charset sseEncodingCharset; - - static { - try { - sseEncodingCharset = StandardCharsets.UTF_8; - } catch (Exception e) { - logger.log(Level.SEVERE, "UTF-8 charset not available. Since SSE only contains UTF-8 data, we can not read SSE data."); - sseEncodingCharset = null; - } - } - - private enum State { - SkipColonAndWhiteSpaces,// Skip colon and all whitespaces after reading field name. - SkipLineDelimitersAndSpaces,// Skip all line delimiters after field value end. - DiscardTillEOL,// On recieving an illegal/unidentified field, ignore everything till EOL. - ReadFieldName, // Read till a colon to get the name of the field. - ReadFieldValue // Read value till the line delimiter. - } - - /** - * Release of these buffers happens in the following ways: - * - * 1) If this was a data buffer, it is released when ServerSentEvent is released. - * 2) If this was an eventId buffer, it is released when next Id arrives or when the connection - * is closed. - * 3) If this was an eventType buffer, it is released when next type arrives or when the connection - * is closed. - */ - private ByteBuf lastEventId; - private ByteBuf lastEventType; - private ByteBuf incompleteData; // Can be field value of name, according to the current state. - - private Type currentFieldType; - - private State state = State.ReadFieldName; - - @Override - protected void decode(ChannelHandlerContext ctx, HttpContent httpContent, List out) { - - final ByteBuf in = httpContent.content(); - - if (null == sseEncodingCharset) { - throw new IllegalArgumentException("Can not read SSE data as UTF-8 charset is not available."); - } - - while (in.isReadable()) { - - final int readerIndexAtStart = in.readerIndex(); - - switch (state) { - case SkipColonAndWhiteSpaces: - if (skipColonAndWhiteSpaces(in)) { - state = State.ReadFieldValue; - } - break; - case SkipLineDelimitersAndSpaces: - if (skipLineDelimiters(in)) { - state = State.ReadFieldName; - } - break; - case DiscardTillEOL: - if(skipTillEOL(in)) { - state = State.SkipLineDelimitersAndSpaces; - } - break; - case ReadFieldName: - final int indexOfColon = scanAndFindColon(in); - - if (-1 == indexOfColon) { // No colon found - // Accumulate data into the field name buffer. - if (null == incompleteData) { - incompleteData = ctx.alloc().buffer(); - } - // accumulate into incomplete data buffer to be used when the full data arrives. - incompleteData.writeBytes(in); - } else { - int fieldNameLengthInTheCurrentBuffer = indexOfColon - readerIndexAtStart; - - ByteBuf fieldNameBuffer; - if (null != incompleteData) { - // Read the remaining data into the temporary buffer - in.readBytes(incompleteData, fieldNameLengthInTheCurrentBuffer); - fieldNameBuffer = incompleteData; - incompleteData = null; - } else { - // Consume the data from the input buffer. - fieldNameBuffer = ctx.alloc().buffer(fieldNameLengthInTheCurrentBuffer, - fieldNameLengthInTheCurrentBuffer); - in.readBytes(fieldNameBuffer, fieldNameLengthInTheCurrentBuffer); - } - - state = State.SkipColonAndWhiteSpaces; // We have read the field name, next we should skip colon & WS. - try { - currentFieldType = readCurrentFieldTypeFromBuffer(fieldNameBuffer); - } finally { - if (null == currentFieldType) { - state = State.DiscardTillEOL; // Ignore this event completely. - } - fieldNameBuffer.release(); - } - } - break; - case ReadFieldValue: - - final int endOfLineStartIndex = scanAndFindEndOfLine(in); - - - if (-1 == endOfLineStartIndex) { // End of line not found, accumulate data into a temporary buffer. - if (null == incompleteData) { - incompleteData = ctx.alloc().buffer(in.readableBytes()); - } - // accumulate into incomplete data buffer to be used when the full data arrives. - incompleteData.writeBytes(in); - } else { // Read the data till end of line into the value buffer. - final int bytesAvailableInThisIteration = endOfLineStartIndex - readerIndexAtStart; - if (null == incompleteData) { - incompleteData = ctx.alloc().buffer(bytesAvailableInThisIteration, - bytesAvailableInThisIteration); - } - incompleteData.writeBytes(in, bytesAvailableInThisIteration); - - switch (currentFieldType) { - case Data: - if (incompleteData.isReadable()) { - out.add(ServerSentEvent.withEventIdAndType(lastEventId, lastEventType, - incompleteData)); - } else { - incompleteData.release(); - } - break; - case Id: - if (incompleteData.isReadable()) { - lastEventId = incompleteData; - } else { - incompleteData.release(); - lastEventId = null; - } - break; - case EventType: - if (incompleteData.isReadable()) { - lastEventType = incompleteData; - } else { - incompleteData.release(); - lastEventType = null; - } - break; - } - /* - * Since all data is read, reset the incomplete data to null. Release of this buffer happens in - * the following ways - * 1) If this was a data buffer, it is released when ServerSentEvent is released. - * 2) If this was an eventId buffer, it is released when next Id arrives or when the connection - * is closed. - * 3) If this was an eventType buffer, it is released when next type arrives or when the connection - * is closed. - */ - incompleteData = null; - state = State.SkipLineDelimitersAndSpaces; // Skip line delimiters after reading a field value completely. - } - break; - } - } - - - if (httpContent instanceof LastHttpContent) { - ctx.fireChannelRead(httpContent); // Since the content is already consumed above (by the SSEDecoder), this is just - // as sending just trailing headers. This is critical to mark the end of stream. - } - } - - private static ServerSentEvent.Type readCurrentFieldTypeFromBuffer(final ByteBuf fieldNameBuffer) { - /* - * This code tries to eliminate the need of creating a string from the ByteBuf as the field names are very - * constrained. The algorithm is as follows: - * - * -- Scan the bytes in the buffer. - * -- Ignore an leading whitespaces - * -- If the first byte matches the expected field names then use the matching field name char array to verify - * the rest of the field name. - * -- If the first byte does not match, reject the field name. - * -- After the first byte, exact match the rest of the field name with the expected field name, byte by byte. - * -- If the name does not exactly match the expected value, then reject the field name. - */ - ServerSentEvent.Type toReturn = ServerSentEvent.Type.Data; - skipLineDelimiters(fieldNameBuffer); - int readableBytes = fieldNameBuffer.readableBytes(); - final int readerIndexAtStart = fieldNameBuffer.readerIndex(); - char[] fieldNameToVerify = DATA_FIELD_NAME; - boolean verified = false; - int actualFieldNameIndexToCheck = 0; // Starts with 1 as the first char is validated by equality. - for (int i = readerIndexAtStart; i < readerIndexAtStart + readableBytes; i++) { - final char charAtI = (char) fieldNameBuffer.getByte(i); - - if (i == readerIndexAtStart) { - switch (charAtI) { // See which among the known field names this buffer belongs. - case 'e': - fieldNameToVerify = EVENT_ID_FIELD_NAME; - toReturn = ServerSentEvent.Type.EventType; - break; - case 'd': - fieldNameToVerify = DATA_FIELD_NAME; - toReturn = ServerSentEvent.Type.Data; - break; - case 'i': - fieldNameToVerify = ID_FIELD_NAME; - toReturn = ServerSentEvent.Type.Id; - break; - default: - return null; - } - } else { - if (++actualFieldNameIndexToCheck >= fieldNameToVerify.length || charAtI != fieldNameToVerify[actualFieldNameIndexToCheck]) { - // If the character does not match or the buffer is bigger than the expected name, then discard. - verified = false; - break; - } else { - // Verified till now. If all characters are matching then this stays as verified, else changed to false. - verified = true; - } - } - } - - if (verified) { - return toReturn; - } else { - return null; - } - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - super.channelInactive(ctx); - if (null != lastEventId) { - lastEventId.release(); - } - if (null != lastEventType) { - lastEventType.release(); - } - if (null != incompleteData) { - incompleteData.release(); - } - } - - protected static int scanAndFindColon(ByteBuf byteBuf) { - return byteBuf.forEachByte(SCAN_COLON_PROCESSOR); - } - - protected static int scanAndFindEndOfLine(ByteBuf byteBuf) { - return byteBuf.forEachByte(SCAN_EOL_PROCESSOR); - } - - protected static boolean skipLineDelimiters(ByteBuf byteBuf) { - return skipTillMatching(byteBuf, SKIP_LINE_DELIMITERS_AND_SPACES_PROCESSOR); - } - - protected static boolean skipColonAndWhiteSpaces(ByteBuf byteBuf) { - return skipTillMatching(byteBuf, SKIP_COLON_AND_WHITE_SPACE_PROCESSOR); - } - - private static boolean skipTillEOL(ByteBuf in) { - return skipTillMatching(in, SKIP_TILL_LINE_DELIMITER_PROCESSOR); - } - - protected static boolean skipTillMatching(ByteBuf byteBuf, ByteProcessor processor) { - final int lastIndexProcessed = byteBuf.forEachByte(processor); - if (-1 == lastIndexProcessed) { - byteBuf.readerIndex(byteBuf.readerIndex() + byteBuf.readableBytes()); // If all the remaining bytes are to be ignored, discard the buffer. - } else { - byteBuf.readerIndex(lastIndexProcessed); - } - - return -1 != lastIndexProcessed; - } - - protected static boolean isLineDelimiter(char c) { - return c == '\r' || c == '\n'; - } -} \ No newline at end of file diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/sse/server/ServerSentEventEncoder.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/sse/server/ServerSentEventEncoder.java deleted file mode 100644 index 71dd99e..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/sse/server/ServerSentEventEncoder.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.sse.server; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundHandlerAdapter; -import io.netty.channel.ChannelPromise; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.util.ByteProcessor; -import io.reactivex.netty.protocol.http.sse.ServerSentEvent; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; - -/** - * An encoder to handle {@link io.reactivex.netty.protocol.http.sse.ServerSentEvent} encoding for an HTTP server. - * - * This encoder will encode any {@link io.reactivex.netty.protocol.http.sse.ServerSentEvent} to {@link ByteBuf} and also set the appropriate HTTP Response - * headers required for SSE - */ -@ChannelHandler.Sharable -public class ServerSentEventEncoder extends ChannelOutboundHandlerAdapter { - - private static final byte[] EVENT_PREFIX_BYTES = "event: ".getBytes(); - private static final byte[] NEW_LINE_AS_BYTES = "\n".getBytes(); - private static final byte[] ID_PREFIX_AS_BYTES = "id: ".getBytes(); - private static final byte[] DATA_PREFIX_AS_BYTES = "data: ".getBytes(); - private final boolean splitSseData; - - public ServerSentEventEncoder() { - this(false); - } - - /** - * Splits the SSE data on new line and create multiple "data" events if {@code splitSseData} is {@code true} - * - * @param splitSseData {@code true} if the SSE data is to be splitted on new line to create multiple "data" events. - */ - public ServerSentEventEncoder(boolean splitSseData) { - this.splitSseData = splitSseData; - } - - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - - Object msgToWriteFurther = msg; - - if (msg instanceof HttpResponse) { - HttpResponse response = (HttpResponse) msg; - /*Set the content-type for SSE*/ - response.headers().set(CONTENT_TYPE, "text/event-stream"); - } else if (msg instanceof ServerSentEvent) { - - final ServerSentEvent serverSentEvent = (ServerSentEvent) msg; - - final ByteBuf out = ctx.alloc().buffer(); - msgToWriteFurther = out; - - if (serverSentEvent.hasEventType()) { // Write event type, if available - out.writeBytes(EVENT_PREFIX_BYTES); - out.writeBytes(serverSentEvent.getEventType()); - out.writeBytes(NEW_LINE_AS_BYTES); - } - - if (serverSentEvent.hasEventId()) { // Write event id, if available - out.writeBytes(ID_PREFIX_AS_BYTES); - out.writeBytes(serverSentEvent.getEventId()); - out.writeBytes(NEW_LINE_AS_BYTES); - } - - final ByteBuf content; - if (serverSentEvent.hasDataAsString()) { - /*Allocate ByteBuf only in the eventloop*/ - content = ctx.alloc().buffer().writeBytes(serverSentEvent.contentAsString().getBytes()); - } else { - content = serverSentEvent.content(); - } - - if (splitSseData) { - while (content.isReadable()) { // Scan the buffer and split on new line into multiple data lines. - final int readerIndexAtStart = content.readerIndex(); - int newLineIndex = content.forEachByte(new ByteProcessor() { - @Override - public boolean process(byte value) throws Exception { - return (char) value != '\n'; - } - }); - if (-1 == newLineIndex) { // No new line, write the buffer as is. - out.writeBytes(DATA_PREFIX_AS_BYTES); - out.writeBytes(content); - out.writeBytes(NEW_LINE_AS_BYTES); - } else { // Write the buffer till the new line and then iterate this loop - out.writeBytes(DATA_PREFIX_AS_BYTES); - out.writeBytes(content, newLineIndex - readerIndexAtStart); - content.readerIndex(content.readerIndex() + 1); - out.writeBytes(NEW_LINE_AS_BYTES); - } - } - } else { // write the buffer with data prefix and new line post fix. - out.writeBytes(DATA_PREFIX_AS_BYTES); - out.writeBytes(content); - out.writeBytes(NEW_LINE_AS_BYTES); - } - } - - ctx.write(msgToWriteFurther, promise); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/util/HttpContentStringLineDecoder.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/util/HttpContentStringLineDecoder.java deleted file mode 100644 index 9891747..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/util/HttpContentStringLineDecoder.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.util; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageDecoder; -import io.netty.handler.codec.http.DefaultLastHttpContent; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.LastHttpContent; -import io.reactivex.netty.util.LineReader; -import io.reactivex.netty.util.StringLineDecoder; - -import java.util.List; - -/** - * A handler just like {@link StringLineDecoder} but works on {@link HttpContent}. This handler will decode the HTTP - * content as lines, separated by a new line. - */ -public class HttpContentStringLineDecoder extends MessageToMessageDecoder { - - private final LineReader reader = new LineReader(); - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - reader.dispose(); - super.handlerRemoved(ctx); - } - - @Override - protected void decode(ChannelHandlerContext ctx, HttpContent msg, List out) throws Exception { - if (msg instanceof LastHttpContent) { - reader.decodeLast(msg.content(), out, ctx.alloc()); - out.add(LastHttpContent.EMPTY_LAST_CONTENT); - } else { - reader.decode(msg.content(), out, ctx.alloc()); - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/WebSocketConnection.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/WebSocketConnection.java deleted file mode 100644 index dba1ebf..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/WebSocketConnection.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.ws; - -import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.reactivex.netty.channel.Connection; -import rx.Observable; -import rx.annotations.Beta; -import rx.functions.Func1; - -/** - * A WebSocket connection which is used to read/write {@link WebSocketFrame}s. - */ -public final class WebSocketConnection { - - private final Connection delegate; - - public WebSocketConnection(Connection delegate) { - this.delegate = delegate; - } - - /** - * Returns the input stream for this connection, until a {@link CloseWebSocketFrame} is received. The terminal - * {@link CloseWebSocketFrame} is included in the returned stream. - * - * @return The input stream for this connection. - */ - public Observable getInput() { - return getInput(true); - } - - /** - * Returns the input stream for this connection. If {@code untilCloseFrame} is {@code true} then the returned stream - * completes after receiving (and emitting) a {@link CloseWebSocketFrame}, otherwise, it completes with an error - * when the underlying channel is closed. - * - * @return The input stream for this connection. - */ - @Beta - public Observable getInput(boolean untilCloseFrame) { - Observable rawInput = delegate.getInput(); - - if (untilCloseFrame) { - return rawInput.takeUntil(new Func1() { - @Override - public Boolean call(WebSocketFrame webSocketFrame) { - return webSocketFrame instanceof CloseWebSocketFrame; - } - }); - } else { - return rawInput; - } - } - - /** - * Writes a stream of frames on this connection. The writes are flushed on completion of the stream, if other flush - * strategies are required, one must use {@link #write(Observable, Func1)} or - * {@link #writeAndFlushOnEach(Observable)} - * - * @param msgs Stream of frames to write. - * - * @return {@link Observable} representing the result of this write. Every subscription to this {@link Observable} - * will replay the write on the channel. - */ - public Observable write(Observable msgs) { - return delegate.write(msgs); - } - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel - * and flushes the channel, everytime, {@code flushSelector} returns {@code true} . Any writes issued before - * subscribing, will also be flushed. However, the returned {@link Observable} will not capture the result of those - * writes, i.e. if the other writes, fail and this write does not, the returned {@link Observable} will not fail. - * - * @param msgs Message stream to write. - * @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. Channel is - * flushed, iff this function returns, {@code true}. - * - * @return An {@link Observable} representing the result of this write. Every - * subscription to this {@link Observable} will write the passed messages and flush all pending writes, when the - * {@code flushSelector} returns {@code true} - */ - public Observable write(Observable msgs, Func1 flushSelector) { - return delegate.write(msgs, flushSelector); - } - - /** - * On subscription of the returned {@link Observable}, writes the passed message stream on the underneath channel - * and flushes the channel, on every write. Any writes issued before subscribing, will also be flushed. However, the - * returned {@link Observable} will not capture the result of those writes, i.e. if the other writes, fail and this - * write does not, the returned {@link Observable} will not fail. - * - * @param msgs Message stream to write. - * - * @return An {@link Observable} representing the result of this write. Every - * subscription to this {@link Observable} will write the passed messages and flush all pending writes, on every - * write. - */ - public Observable writeAndFlushOnEach(Observable msgs) { - return delegate.writeAndFlushOnEach(msgs); - } - - /** - * Flushes all writes, if any, before calling the flush. - */ - public void flush() { - delegate.flush(); - } - - /** - * Flushes any pending writes and closes the connection. Same as calling {@code close(true)} - * - * @return {@link Observable} representing the result of close. - */ - public Observable close() { - return delegate.close(); - } - - /** - * Closes this channel after flushing all pending writes. - * - * @return {@link Observable} representing the result of close and flush. - */ - public Observable close(boolean flush) { - return delegate.close(flush); - } - - /** - * Returns an {@link Observable} that completes when this connection is closed. - * - * @return An {@link Observable} that completes when this connection is closed. - */ - public Observable closeListener() { - return delegate.closeListener(); - } - - /** - * Closes the connection immediately. Same as calling {@link #close()} and subscribing to the returned - * {@code Observable} - */ - public void closeNow() { - delegate.closeNow(); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/OperatorCacheSingleWebsocketConnection.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/OperatorCacheSingleWebsocketConnection.java deleted file mode 100644 index b0d5f27..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/OperatorCacheSingleWebsocketConnection.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.ws.client; - -import io.reactivex.netty.protocol.http.ws.WebSocketConnection; -import rx.Observable; -import rx.Observable.Operator; -import rx.Subscriber; -import rx.annotations.Experimental; -import rx.functions.Action0; -import rx.functions.Actions; -import rx.functions.Func1; - -/** - * An operator to cache a {@link WebSocketConnection} until it closes, upon which the source that re-creates an HTTP - * upgrade request to get a fresh {@link WebSocketConnection} is subscribed, to refresh the stale connection in the - * cache. - * - * A typical usage example for this operator is: - * -
- {@code
-     HttpClient.newClient(socketAddress)
-               .createGet("/ws")
-               .requestWebSocketUpgrade()
-               .map(WebSocketResponse::getWebSocketConnection)
-               .nest()
-               .lift(new OperatorCacheSingleWebsocketConnection())
- }
- 
- * - * Since multiple subscriptions to {@link WebSocketResponse#getWebSocketConnection()} do not re-run the original HTTP - * upgrade request, this operator expects the source {@code Observable} to be passed to it, so that on close of the - * cached {@link WebSocketConnection}, it can re-subscribe to the original HTTP request and create a fresh connection. - * This is the reason the above code uses {@link Observable#nest()} to get a reference to the source {@code Observable}. - * - *

Cache liveness guarantees

- * - * Although, this operator will make sure that when the cached connection has terminated, the next refresh will - * re-subscribe to the source, there is no guarantee that a dead connection is never emitted from this operator as it - * completely depends on the timing of when the connection terminates and when a new subscription arrives. The two - * events can be concurrent and hence unpredictable. - */ -@Experimental -public class OperatorCacheSingleWebsocketConnection - implements Operator>> { - - private boolean subscribedToSource; /*Guarded by this*/ - private Observable cachedSource; /*Guarded by this*/ - - @Override - public Subscriber>> - call(final Subscriber subscriber) { - - return new Subscriber>>(subscriber) { - - private volatile boolean anItemEmitted; - - @Override - public void onCompleted() { - if (!anItemEmitted) { - subscriber.onError(new IllegalStateException("No Observable emitted from source.")); - } - } - - @Override - public void onError(Throwable e) { - subscriber.onError(e); - } - - @Override - public void onNext(Observable> source) { - anItemEmitted = true; - - /** - * The idea below is for using a single cache {@code Observable} so that the cache operator can cache - * the generated connection. However, when the cached connection is terminated, a new cached source - * must be generated to be used for subsequent subscriptions. - * As the only way to re-run the original HTTP upgrade request, to obtain a fresh connection, is to - * subscribe to the {@code Observable>}, that is the reason the below - * code uses a {@code flatmap} to transform {@code Observable>} to an - * {@code Observable} and still keeping the ability to re-subscribe to the original - * {@code Observable>}. - */ - final Observable _cachedSource; - final Observable o = source.flatMap( - new Func1, Observable>() { - @Override - public Observable call(Observable connSource) { - /*This is for flatmap to subscribe to the nested {@code Observable}*/ - return connSource; - } - }).map(new Func1() { - @Override - public WebSocketConnection call(WebSocketConnection connection) { - Observable lifecycle = connection.closeListener(); - lifecycle = lifecycle.onErrorResumeNext(Observable.empty()) - .doAfterTerminate(new Action0() { - @Override - public void call() { - synchronized (OperatorCacheSingleWebsocketConnection.this) { - // refresh the source on next subscribe - subscribedToSource = false; - } - } - }); - subscriber.add(lifecycle.subscribe(Actions.empty())); - return connection; - } - }).cache(); - - synchronized (OperatorCacheSingleWebsocketConnection.this) { - if (!subscribedToSource) { - subscribedToSource = true; - /*From here on, all subscriptions will use the newly created cached source which on first - subscription will re-run the original HTTP upgrade request and get a fresh WS connection*/ - cachedSource = o; - } - - _cachedSource = cachedSource; - } - - _cachedSource.unsafeSubscribe(subscriber); - } - }; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/WebSocketRequest.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/WebSocketRequest.java deleted file mode 100644 index 9dbb4e3..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/WebSocketRequest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.ws.client; - -import rx.Observable; - -/** - * A WebSocket upgrade HTTP request that will generate a {@link WebSocketResponse} - * - * @param The type of the content received in the HTTP response, in case, the upgrade was rejected by the server. - */ -public abstract class WebSocketRequest extends Observable> { - - protected WebSocketRequest(OnSubscribe> f) { - super(f); - } - - /** - * Specify any sub protocols that are to be requested to the server as specified by the - * specifications - * - * @param subProtocols Sub protocols to request. - * - * @return A new instance of {@link WebSocketRequest} with the sub protocols requested. - */ - public abstract WebSocketRequest requestSubProtocols(String... subProtocols); - - /** - * By default, the websocket request made is for the latest version in the specifications, however, if an earlier - * version is required, it can be updated by this method. - * - * @param version WebSocket version. - * - * @return A new instance of {@link WebSocketRequest} with the version requested. - */ - public abstract WebSocketRequest version(int version); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/WebSocketResponse.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/WebSocketResponse.java deleted file mode 100644 index bce6dd8..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/WebSocketResponse.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.ws.client; - -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.reactivex.netty.protocol.http.client.HttpClientResponse; -import io.reactivex.netty.protocol.http.ws.WebSocketConnection; -import rx.Observable; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; -import static io.netty.handler.codec.http.HttpHeaderValues.*; - -public abstract class WebSocketResponse extends HttpClientResponse { - - public abstract Observable getWebSocketConnection(); - - public String getAcceptedSubProtocol() { - return getHeader(SEC_WEBSOCKET_PROTOCOL); - } - - public boolean isUpgraded() { - return getStatus().equals(HttpResponseStatus.SWITCHING_PROTOCOLS) - && containsHeader(CONNECTION, HttpHeaderValues.UPGRADE, true) - && containsHeader(HttpHeaderNames.UPGRADE, WEBSOCKET, true) - && containsHeader(SEC_WEBSOCKET_ACCEPT); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/Ws7To13UpgradeHandler.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/Ws7To13UpgradeHandler.java deleted file mode 100644 index 7ef1f9d..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/Ws7To13UpgradeHandler.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.ws.client; - -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.ChannelPromise; -import io.netty.handler.codec.http.HttpClientCodec; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.websocketx.WebSocket13FrameDecoder; -import io.netty.handler.codec.http.websocketx.WebSocket13FrameEncoder; -import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker13; -import io.netty.handler.codec.http.websocketx.WebSocketHandshakeException; -import io.netty.util.CharsetUtil; -import io.reactivex.netty.protocol.http.ws.internal.WsUtils; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; -import static io.netty.handler.codec.http.HttpHeaderNames.UPGRADE; -import static io.netty.handler.codec.http.HttpHeaderValues.*; -import static io.reactivex.netty.protocol.http.HttpHandlerNames.*; - -/** - * A channel handler to appropriately setup WebSocket upgrade requests and verify upgrade responses. - * It also updates the pipeline post a successful upgrade. - * - * The handshake code here is taken from {@link WebSocketClientHandshaker13} and not used directly because the APIs - * do not suit our needs. - */ -public class Ws7To13UpgradeHandler extends ChannelDuplexHandler { - - private String expectedChallengeResponseString; - private boolean upgraded; - - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - if (msg instanceof HttpRequest) { - final HttpRequest request = (HttpRequest) msg; - if (request.headers().contains(UPGRADE, WEBSOCKET, false)) { - /* - * We can safely modify the request here as this request is exclusively for WS upgrades and the following - * headers are added for ALL upgrade requests. Since, the handler is single-threaded, these updates do not - * step on each other. - */ - // Get 16 bit nonce and base 64 encode it - byte[] nonce = WsUtils.randomBytes(16); - String key = WsUtils.base64(nonce); - request.headers().set(SEC_WEBSOCKET_KEY, key); - String acceptSeed = key + WebSocketClientHandshaker13.MAGIC_GUID; - byte[] sha1 = WsUtils.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); - expectedChallengeResponseString = WsUtils.base64(sha1); - String hostHeader = request.headers().get(HOST); - if (null != hostHeader) { - request.headers().set(SEC_WEBSOCKET_ORIGIN, "http://" + hostHeader); - } - final ChannelHandlerContext clientCodecCtx = ctx.pipeline().context(HttpClientCodec.getName()); - if (null == clientCodecCtx) { - promise.tryFailure(new IllegalStateException( - "Http client codec not found, can not upgrade to WebSockets.")); - return; - } - - final HttpClientCodec codec = (HttpClientCodec) clientCodecCtx.handler(); - - promise.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (future.isSuccess()) { - ChannelPipeline p = future.channel().pipeline(); - // Remove the encoder part of the codec as the user may start writing frames after this method returns. - p.addAfter(clientCodecCtx.name(), WsClientEncoder.getName(), - new WebSocket13FrameEncoder(true/*Clients must set this to true*/)); - } - } - }); - } - } - super.write(ctx, msg, promise); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (isUpgradeResponse(msg)) { - final HttpResponse response = (HttpResponse) msg; - /*Other verifications are done by WebSocketResponse itself.*/ - String accept = response.headers().get(SEC_WEBSOCKET_ACCEPT); - if (accept == null || !accept.equals(expectedChallengeResponseString)) { - throw new WebSocketHandshakeException(String.format( - "Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString)); - } - - final ChannelPipeline pipeline = ctx.pipeline(); - ChannelHandlerContext codecCtx = pipeline.context(HttpClientCodec.getName()); - - if (null == codecCtx) { - throw new IllegalStateException("Http codec not found, can not upgrade to WebSocket."); - } - - pipeline.addAfter(codecCtx.name(), WsClientDecoder.getName(), - new WebSocket13FrameDecoder(false/*Clients must set this to false*/, false, - 65555));//TODO: Fix me - pipeline.remove(HttpClientCodec.class); - upgraded = true; - } - - if (upgraded && msg instanceof HttpContent) { - /*Ignore Content once upgraded. The content should not come typically since an Upgrade accept response is - empty. The only HttpContent that would come is an empty LastHttpContent that netty generates.*/ - ((HttpContent)msg).release(); - return; - } - - super.channelRead(ctx, msg); - } - - private static boolean isUpgradeResponse(Object msg) { - if (msg instanceof HttpResponse) { - HttpResponse response = (HttpResponse) msg; - HttpHeaders headers = response.headers(); - return response.status().equals(HttpResponseStatus.SWITCHING_PROTOCOLS) - && headers.contains(CONNECTION, HttpHeaderValues.UPGRADE, true) - && headers.contains(UPGRADE, WEBSOCKET, true) - && headers.contains(SEC_WEBSOCKET_ACCEPT); - } - return false; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/internal/WebSocketRequestImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/internal/WebSocketRequestImpl.java deleted file mode 100644 index 0e88d95..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/internal/WebSocketRequestImpl.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.ws.client.internal; - -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.websocketx.WebSocketVersion; -import io.reactivex.netty.protocol.http.client.HttpClientRequest; -import io.reactivex.netty.protocol.http.client.HttpClientResponse; -import io.reactivex.netty.protocol.http.client.internal.HttpClientRequestImpl; -import io.reactivex.netty.protocol.http.client.internal.RawRequest; -import io.reactivex.netty.protocol.http.ws.client.WebSocketRequest; -import io.reactivex.netty.protocol.http.ws.client.WebSocketResponse; -import rx.Subscriber; -import rx.functions.Func1; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; -import static io.netty.handler.codec.http.HttpHeaderValues.*; - -public final class WebSocketRequestImpl extends WebSocketRequest { - - private final String[] subProtocolsRequested; - private final WebSocketVersion version; - private final HttpClientRequest httpRequest; - - private WebSocketRequestImpl(final HttpClientRequest httpRequest) { - this(httpRequest, null, WebSocketVersion.V13); - } - - private WebSocketRequestImpl(final HttpClientRequest httpRequest, String[] subProtocolsRequested, - WebSocketVersion version) { - super(new OnSubscribe>() { - @Override - public void call(Subscriber> subscriber) { - httpRequest.map(new Func1, WebSocketResponseImpl>() { - @Override - public WebSocketResponseImpl call(HttpClientResponse response) { - return new WebSocketResponseImpl<>(response); - } - }).unsafeSubscribe(subscriber); - } - }); - this.httpRequest = httpRequest; - this.subProtocolsRequested = subProtocolsRequested; - this.version = version; - } - - public String[] getSubProtocolsRequested() { - return subProtocolsRequested; - } - - @Override - public WebSocketRequestImpl requestSubProtocols(String... subProtocols) { - return new WebSocketRequestImpl<>(httpRequest.setHeader(SEC_WEBSOCKET_PROTOCOL, - expectedSubProtocol(subProtocols)), subProtocols, - version); - } - - @Override - public WebSocketRequestImpl version(int version) { - WebSocketVersion webSocketVersion; - - switch (version) { - case 7: - webSocketVersion = WebSocketVersion.V07; - break; - case 8: - webSocketVersion = WebSocketVersion.V08; - break; - case 13: - webSocketVersion = WebSocketVersion.V13; - break; - default: - webSocketVersion = WebSocketVersion.UNKNOWN; - break; - } - return new WebSocketRequestImpl<>(httpRequest.setHeader(SEC_WEBSOCKET_VERSION, version), - subProtocolsRequested, webSocketVersion); - } - - public static WebSocketRequestImpl createNew(final HttpClientRequestImpl httpRequest) { - /*This makes a copy of the request so that we can safely make modifications to the underlying headers.*/ - @SuppressWarnings("unchecked") - final HttpClientRequestImpl upgradeRequest = - (HttpClientRequestImpl) httpRequest.addHeader(HttpHeaderNames.UPGRADE, WEBSOCKET); - RawRequest rawRequest = upgradeRequest.unsafeRawRequest(); - HttpRequest headers = rawRequest.getHeaders(); - headers.headers().add(CONNECTION, HttpHeaderValues.UPGRADE); - headers.headers().add(SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue()); - - return new WebSocketRequestImpl<>(upgradeRequest); - } - - private static String expectedSubProtocol(String[] subProtocols) { - if (null == subProtocols || subProtocols.length == 0) { - return null; - } - - if (subProtocols.length == 1) { - return subProtocols[0]; - } - - StringBuilder builder = new StringBuilder(); - for (String subProtocol : subProtocols) { - if (builder.length() != 0) { - builder.append(','); - } - builder.append(subProtocol); - } - return builder.toString(); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/internal/WebSocketResponseImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/internal/WebSocketResponseImpl.java deleted file mode 100644 index 5f6d2e1..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/client/internal/WebSocketResponseImpl.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.ws.client.internal; - -import io.netty.channel.Channel; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.cookie.Cookie; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.ContentSource; -import io.reactivex.netty.client.ClientConnectionToChannelBridge; -import io.reactivex.netty.protocol.http.client.HttpClientResponse; -import io.reactivex.netty.protocol.http.internal.AbstractHttpConnectionBridge; -import io.reactivex.netty.protocol.http.sse.ServerSentEvent; -import io.reactivex.netty.protocol.http.ws.WebSocketConnection; -import io.reactivex.netty.protocol.http.ws.client.WebSocketResponse; -import rx.Observable; -import rx.Observable.Transformer; - -import java.text.ParseException; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -public final class WebSocketResponseImpl extends WebSocketResponse { - - private final HttpClientResponse delegate; - private final WebSocketConnection wsConnection; - private final Channel channel; - - public WebSocketResponseImpl(HttpClientResponse delegate) { - this.delegate = delegate; - @SuppressWarnings("unchecked") - Connection cast = - (Connection) delegate.unsafeConnection(); - channel = cast.unsafeNettyChannel(); - wsConnection = new WebSocketConnection(cast); - } - - @Override - public Observable getWebSocketConnection() { - if (isUpgraded()) { - /*Do not pool connection once upgraded to WS. A closing handshake closes the channel*/ - channel.attr(ClientConnectionToChannelBridge.DISCARD_CONNECTION).set(true); - channel.attr(AbstractHttpConnectionBridge.CONNECTION_UPGRADED).set(true); - return Observable.just(wsConnection); - } else { - return Observable.error(new IllegalStateException("WebSocket upgrade rejected by the server.")); - } - } - - @Override - public HttpVersion getHttpVersion() { - return delegate.getHttpVersion(); - } - - @Override - public HttpResponseStatus getStatus() { - return delegate.getStatus(); - } - - @Override - public Map> getCookies() { - return delegate.getCookies(); - } - - @Override - public boolean containsHeader(CharSequence name) { - return delegate.containsHeader(name); - } - - @Override - public boolean containsHeader(CharSequence name, CharSequence value, boolean ignoreCaseValue) { - return delegate.containsHeader(name, value, ignoreCaseValue); - } - - @Override - public Iterator> headerIterator() { - return delegate.headerIterator(); - } - - @Override - public String getHeader(CharSequence name) { - return delegate.getHeader(name); - } - - @Override - public String getHeader(CharSequence name, String defaultValue) { - return delegate.getHeader(name, defaultValue); - } - - @Override - public List getAllHeaderValues(CharSequence name) { - return delegate.getAllHeaderValues(name); - } - - @Override - public long getContentLength() { - return delegate.getContentLength(); - } - - @Override - public long getContentLength(long defaultValue) { - return delegate.getContentLength(defaultValue); - } - - @Override - public long getDateHeader(CharSequence name) throws ParseException { - return delegate.getDateHeader(name); - } - - @Override - public long getDateHeader(CharSequence name, long defaultValue) { - return delegate.getDateHeader(name, defaultValue); - } - - @Override - public String getHostHeader() { - return delegate.getHostHeader(); - } - - @Override - public String getHost(String defaultValue) { - return delegate.getHost(defaultValue); - } - - @Override - public int getIntHeader(CharSequence name) { - return delegate.getIntHeader(name); - } - - @Override - public int getIntHeader(CharSequence name, int defaultValue) { - return delegate.getIntHeader(name, defaultValue); - } - - @Override - public boolean isContentLengthSet() { - return delegate.isContentLengthSet(); - } - - @Override - public boolean isKeepAlive() { - return delegate.isKeepAlive(); - } - - @Override - public boolean isTransferEncodingChunked() { - return delegate.isTransferEncodingChunked(); - } - - @Override - public Set getHeaderNames() { - return delegate.getHeaderNames(); - } - - @Override - public HttpClientResponse addHeader(CharSequence name, - Object value) { - return delegate.addHeader(name, value); - } - - @Override - public HttpClientResponse addCookie( - Cookie cookie) { - return delegate.addCookie(cookie); - } - - @Override - public HttpClientResponse addDateHeader(CharSequence name, - Date value) { - return delegate.addDateHeader(name, value); - } - - @Override - public HttpClientResponse addDateHeader(CharSequence name, - Iterable values) { - return delegate.addDateHeader(name, values); - } - - @Override - public HttpClientResponse addHeader(CharSequence name, - Iterable values) { - return delegate.addHeader(name, values); - } - - @Override - public HttpClientResponse setDateHeader(CharSequence name, - Date value) { - return delegate.setDateHeader(name, value); - } - - @Override - public HttpClientResponse setHeader(CharSequence name, - Object value) { - return delegate.setHeader(name, value); - } - - @Override - public HttpClientResponse setDateHeader(CharSequence name, - Iterable values) { - return delegate.setDateHeader(name, values); - } - - @Override - public HttpClientResponse setHeader(CharSequence name, - Iterable values) { - return delegate.setHeader(name, values); - } - - @Override - public HttpClientResponse removeHeader(CharSequence name) { - return delegate.removeHeader(name); - } - - @Override - public ContentSource getContentAsServerSentEvents() { - return delegate.getContentAsServerSentEvents(); - } - - @Override - public ContentSource getContent() { - return delegate.getContent(); - } - - @Override - public Observable discardContent() { - return delegate.discardContent(); - } - - @Override - public HttpClientResponse transformContent(Transformer transformer) { - return delegate.transformContent(transformer); - } - - @Override - public Channel unsafeNettyChannel() { - return delegate.unsafeNettyChannel(); - } - - @Override - public Connection unsafeConnection() { - return delegate.unsafeConnection(); - } - - @Override - public String toString() { - return delegate.toString(); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/internal/WsUtils.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/internal/WsUtils.java deleted file mode 100644 index 6719277..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/internal/WsUtils.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.ws.internal; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.base64.Base64; -import io.netty.util.CharsetUtil; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -/** - * This is a copy of relevant methods from WebSocketUtil in netty as that class is not public. - */ -public final class WsUtils { - - private WsUtils() { - } - - /** - * Performs a SHA-1 hash on the specified data - * - * @param data The data to hash - * @return The hashed data - */ - public static byte[] sha1(byte[] data) { - try { - //Attempt to get a MessageDigest that uses SHA1 - MessageDigest md = MessageDigest.getInstance("SHA1"); - //Hash the data - return md.digest(data); - } catch (NoSuchAlgorithmException e) { - //Alright, you might have an old system. - throw new InternalError("SHA-1 is not supported on this platform - Outdated?"); - } - } - - /** - * Performs base64 encoding on the specified data - * - * @param data The data to encode - * @return An encoded string containing the data - */ - public static String base64(byte[] data) { - ByteBuf encodedData = Unpooled.wrappedBuffer(data); - ByteBuf encoded = Base64.encode(encodedData); - String encodedString = encoded.toString(CharsetUtil.UTF_8); - encoded.release(); - return encodedString; - } - - /** - * Creates an arbitrary number of random bytes - * - * @param size the number of random bytes to create - * @return An array of random bytes - */ - public static byte[] randomBytes(int size) { - byte[] bytes = new byte[size]; - - for (int index = 0; index < size; index++) { - bytes[index] = (byte) randomNumber(0, 255); - } - - return bytes; - } - - /** - * Generates a pseudo-random number - * - * @param minimum The minimum allowable value - * @param maximum The maximum allowable value - * @return A pseudo-random number - */ - public static int randomNumber(int minimum, int maximum) { - return (int) (Math.random() * maximum + minimum); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/V7to13Handshaker.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/V7to13Handshaker.java deleted file mode 100644 index e42006d..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/V7to13Handshaker.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.ws.server; - -import io.netty.handler.codec.http.websocketx.WebSocketVersion; -import io.reactivex.netty.protocol.http.server.HttpServerRequest; -import io.reactivex.netty.protocol.http.server.HttpServerResponse; -import io.reactivex.netty.protocol.http.ws.server.Ws7To13UpgradeHandler.WebSocket7To13UpgradeAcceptedEvent; -import rx.Subscriber; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; - -/** - * Implementation of {@link WebSocketHandshaker} for web socket spec versions 7.0 to 13.0 (includes final RFC) - */ -final class V7to13Handshaker extends WebSocketHandshaker { - - private final State state; - private final HttpServerRequest request; - private final WebSocketHandler handler; - - private V7to13Handshaker(final State state, final HttpServerRequest request, final WebSocketHandler handler) { - super(new OnSubscribe() { - @Override - public void call(Subscriber subscriber) { - state.upgradeResponse.unsafeNettyChannel() - .pipeline() - .fireUserEventTriggered(new WebSocket7To13UpgradeAcceptedEvent(subscriber, handler, - state, request)); - } - }); - this.state = state; - this.request = request; - this.handler = handler; - } - - @Override - public WebSocketHandshaker subprotocol(String... subprotocols) { - return new V7to13Handshaker(new State(state, subprotocols), request, handler); - } - - @Override - public WebSocketHandshaker allowExtensions(boolean allowExtensions) { - return new V7to13Handshaker(new State(state, allowExtensions), request, handler); - } - - @Override - public WebSocketHandshaker location(String webSocketLocation) { - return new V7to13Handshaker(new State(state, webSocketLocation), request, handler); - } - - @Override - public WebSocketHandshaker maxFramePayloadLength(int maxFramePayloadLength) { - return new V7to13Handshaker(new State(state, maxFramePayloadLength), request, handler); - } - - static V7to13Handshaker createNew(WebSocketVersion version, HttpServerRequest request, - HttpServerResponse upgradeResponse, WebSocketHandler handler) { - return new V7to13Handshaker(new State(version, request, upgradeResponse), request, handler); - } - - /*package-private, used by upgrade handler*/static final class State { - - private final WebSocketVersion version; - private final HttpServerResponse upgradeResponse; - private final String[] supportedSubProtocols; - private final String locationForV00; - private final boolean allowExtensions; - private final int maxFramePayloadLength; - private final String secWSkey; - private final String requestSubProtocols; - - private State(WebSocketVersion version, HttpServerRequest request, HttpServerResponse upgradeResponse) { - this(getKey(request), getRequestedProtocols(request), version, upgradeResponse, null, null, - DEFAULT_ALLOW_EXTENSIONS, DEFAULT_MAX_FRAME_PAYLOAD_LENGTH); - } - - private State(State current, String... subprotocols) { - this(current.secWSkey, current.requestSubProtocols, current.version, current.upgradeResponse, - subprotocols, current.locationForV00, current.allowExtensions, current.maxFramePayloadLength); - } - - private State(State current, int maxFramePayloadLength) { - this(current.secWSkey, current.requestSubProtocols, current.version, current.upgradeResponse, - current.supportedSubProtocols, current.locationForV00, current.allowExtensions, - maxFramePayloadLength); - } - - private State(State current, boolean allowExtensions) { - this(current.secWSkey, current.requestSubProtocols, current.version, current.upgradeResponse, - current.supportedSubProtocols, current.locationForV00, allowExtensions, - current.maxFramePayloadLength); - } - - private State(String secWSkey, String requestSubProtocols, WebSocketVersion version, - HttpServerResponse upgradeResponse, String[] supportedSubProtocols, String locationForV00, - boolean allowExtensions, int maxFramePayloadLength) { - this.secWSkey = secWSkey; - this.requestSubProtocols = requestSubProtocols; - this.version = version; - this.upgradeResponse = upgradeResponse; - this.supportedSubProtocols = supportedSubProtocols; - this.locationForV00 = locationForV00; - this.allowExtensions = allowExtensions; - this.maxFramePayloadLength = maxFramePayloadLength; - } - - private static String getRequestedProtocols(HttpServerRequest request) { - return request.getHeader(SEC_WEBSOCKET_PROTOCOL); - } - - private static String getKey(HttpServerRequest request) { - return request.getHeader(SEC_WEBSOCKET_KEY); - } - - public WebSocketVersion getVersion() { - return version; - } - - public HttpServerResponse getUpgradeResponse() { - return upgradeResponse; - } - - public String[] getSupportedSubProtocols() { - return supportedSubProtocols; - } - - public String getLocationForV00() { - return locationForV00; - } - - public boolean isAllowExtensions() { - return allowExtensions; - } - - public int getMaxFramePayloadLength() { - return maxFramePayloadLength; - } - - public String getSecWSkey() { - return secWSkey; - } - - public String getRequestSubProtocols() { - return requestSubProtocols; - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/WebSocketHandler.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/WebSocketHandler.java deleted file mode 100644 index 5d42baa..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/WebSocketHandler.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.ws.server; - -import io.reactivex.netty.protocol.http.ws.WebSocketConnection; -import rx.Observable; - -/** - * A handler for {@link WebSocketConnection} upon a successful upgrade from an HTTP request. - */ -public interface WebSocketHandler { - - /** - * Processes the passed connection. - * - * @param wsConnection Connection to process. - * - * @return {@code Observable} representing the processing termination. - */ - Observable handle(WebSocketConnection wsConnection); - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/WebSocketHandlers.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/WebSocketHandlers.java deleted file mode 100644 index 1ee9e3e..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/WebSocketHandlers.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.ws.server; - -import io.netty.handler.codec.http.HttpResponseStatus; -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 rx.Observable; - -/** - * A utility to provide convenience {@link RequestHandler} implementations for Web Sockets. - */ -public final class WebSocketHandlers { - - private WebSocketHandlers() { - } - - /** - * Returns a {@link RequestHandler} that accepts all WebSocket upgrade requests by delegating it to the passed - * handler but sends an HTTP 404 response for all other requests. - * - * @param handler Web Socket handler for all web socket upgrade requests. - * - * @return request handler. - */ - public static RequestHandler acceptAllUpgrades(final WebSocketHandler handler) { - return new RequestHandler() { - @Override - public Observable handle(HttpServerRequest request, HttpServerResponse response) { - if (request.isWebSocketUpgradeRequested()) { - return response.acceptWebSocketUpgrade(handler); - } - - return response.setStatus(HttpResponseStatus.NOT_FOUND) - .write(Observable.empty()); - } - }; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/WebSocketHandshaker.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/WebSocketHandshaker.java deleted file mode 100644 index afe5ef5..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/WebSocketHandshaker.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.ws.server; - -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker; -import io.netty.handler.codec.http.websocketx.WebSocketVersion; -import io.netty.util.internal.StringUtil; -import io.reactivex.netty.protocol.http.server.HttpServerRequest; -import io.reactivex.netty.protocol.http.server.HttpServerResponse; -import rx.Observable; -import rx.Subscriber; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; -import static io.netty.handler.codec.http.HttpHeaderValues.*; -/** - * The websocket handshaker for sending handshake response back to the client. - * - * The defaults chosen by the handshaker can be altered by using the various methods here. - */ -public abstract class WebSocketHandshaker extends Observable { - - public static final int DEFAULT_MAX_FRAME_PAYLOAD_LENGTH = 65536; - public static final boolean DEFAULT_ALLOW_EXTENSIONS = true; - - protected WebSocketHandshaker(OnSubscribe f) { - super(f); - } - - public abstract WebSocketHandshaker subprotocol(String... subprotocols); - - public abstract WebSocketHandshaker allowExtensions(boolean allowExtensions); - - public abstract WebSocketHandshaker location(String webSocketLocation); - - public abstract WebSocketHandshaker maxFramePayloadLength(int maxFramePayloadLength); - - public static WebSocketHandshaker newHandshaker(HttpServerRequest request, - HttpServerResponse upgradeResponse, WebSocketHandler handler) { - final WebSocketVersion wsVersion = getWsVersion(request); - return V7to13Handshaker.createNew(wsVersion, request, upgradeResponse, handler); - } - - public static WebSocketHandshaker newErrorHandshaker(Throwable error) { - return new ErrorWebSocketHandshaker(error); - } - - /** - * This is copied from {@link WebSocketServerHandshaker} - * - * Selects the first matching supported sub protocol - * - * @param requestedSubprotocols CSV of protocols to be supported. e.g. "chat, superchat" - * @return First matching supported sub protocol. Null if not found. - */ - protected static String selectSubprotocol(String requestedSubprotocols, String[] supportedSubProtocols) { - if (requestedSubprotocols == null || supportedSubProtocols.length == 0) { - return null; - } - - String[] requestedSubprotocolArray = requestedSubprotocols.split(","); - - for (String p: requestedSubprotocolArray) { - String requestedSubprotocol = p.trim(); - - for (String supportedSubprotocol: supportedSubProtocols) { - if (WebSocketServerHandshaker.SUB_PROTOCOL_WILDCARD.equals(supportedSubprotocol) - || requestedSubprotocol.equals(supportedSubprotocol)) { - return requestedSubprotocol; - } - } - } - - // No match found - return null; - } - - public static boolean isUpgradeRequested(HttpServerRequest upgradeRequest) { - return null != upgradeRequest && upgradeRequest.containsHeader(HttpHeaderNames.UPGRADE) - && WEBSOCKET.contentEqualsIgnoreCase(upgradeRequest.getHeader(HttpHeaderNames.UPGRADE)); - } - - public static boolean isUpgradeRequested(HttpRequest upgradeRequest) { - return null != upgradeRequest && upgradeRequest.headers().contains(HttpHeaderNames.UPGRADE) - && WEBSOCKET.contentEqualsIgnoreCase(upgradeRequest.headers() - .get(HttpHeaderNames.UPGRADE)); - } - - private static WebSocketVersion getWsVersion(HttpServerRequest request) { - String version = request.getHeader(SEC_WEBSOCKET_VERSION); - - switch (version) { - case "0": - return WebSocketVersion.V00; - case "7": - return WebSocketVersion.V07; - case "8": - return WebSocketVersion.V08; - case "13": - return WebSocketVersion.V13; - default: - return WebSocketVersion.UNKNOWN; - } - } - - private static class ErrorWebSocketHandshaker extends WebSocketHandshaker { - - public ErrorWebSocketHandshaker(final Throwable error) { - super(new OnSubscribe() { - @Override - public void call(Subscriber subscriber) { - subscriber.onError(error); - } - }); - } - - @Override - public WebSocketHandshaker subprotocol(String... subprotocols) { - return this; - } - - @Override - public WebSocketHandshaker allowExtensions(boolean allowExtensions) { - return this; - } - - @Override - public WebSocketHandshaker location(String webSocketLocation) { - return this; - } - - @Override - public WebSocketHandshaker maxFramePayloadLength(int maxFramePayloadLength) { - return this; - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/Ws7To13UpgradeHandler.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/Ws7To13UpgradeHandler.java deleted file mode 100644 index 4779be2..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/http/ws/server/Ws7To13UpgradeHandler.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.ws.server; - -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocket13FrameDecoder; -import io.netty.handler.codec.http.websocketx.WebSocket13FrameEncoder; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrameDecoder; -import io.netty.handler.codec.http.websocketx.WebSocketFrameEncoder; -import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker07; -import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker08; -import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker13; -import io.netty.util.CharsetUtil; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.MarkAwarePipeline; -import io.reactivex.netty.protocol.http.internal.AbstractHttpConnectionBridge; -import io.reactivex.netty.protocol.http.server.HttpServerRequest; -import io.reactivex.netty.protocol.http.server.HttpServerResponse; -import io.reactivex.netty.protocol.http.ws.WebSocketConnection; -import io.reactivex.netty.protocol.http.ws.internal.WsUtils; -import io.reactivex.netty.protocol.http.ws.server.V7to13Handshaker.State; -import rx.Observable; -import rx.Observable.OnSubscribe; -import rx.Subscriber; -import rx.functions.Action0; -import rx.functions.Func0; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; -import static io.netty.handler.codec.http.HttpHeaderValues.*; -import static io.reactivex.netty.protocol.http.HttpHandlerNames.*; - -/** - * A websocket upgrade handler for upgrading to WebSocket versions 7 to 13. This handler listens for - * {@link WebSocket7To13UpgradeAcceptedEvent} and upon recieving such an event, it sets up the - * {@link WebSocketConnection} to hand it over to the associated {@link WebSocketHandler} - */ -public final class Ws7To13UpgradeHandler extends ChannelDuplexHandler { - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - - if (evt instanceof WebSocket7To13UpgradeAcceptedEvent) { - - final WebSocket7To13UpgradeAcceptedEvent wsUpEvt = (WebSocket7To13UpgradeAcceptedEvent) evt; - - final State state = wsUpEvt.state; - final Subscriber subscriber = wsUpEvt.acceptUpgradeSubscriber; - - String errorIfAny = configureResponseForWs(state); - - if (null != errorIfAny) { - subscriber.onError(new IllegalStateException(errorIfAny)); - return; - } - - final MarkAwarePipeline pipeline = state.getUpgradeResponse().unsafeConnection() - .getResettableChannelPipeline(); - - @SuppressWarnings("unchecked") - final Connection wsConn = - (Connection) wsUpEvt.state.getUpgradeResponse().unsafeConnection(); - - wsUpEvt.request.discardContent() - .onErrorResumeNext(Observable.empty()) // In case, the request content was read, ignore. - .concatWith(state.getUpgradeResponse().setTransferEncodingChunked().sendHeaders()) - .doOnCompleted(new Action0() { - @Override - public void call() { - /*We are no more talking HTTP*/ - pipeline.remove(HttpServerEncoder.getName()); - pipeline.remove(HttpServerDecoder.getName()); - pipeline.channel().attr(AbstractHttpConnectionBridge.CONNECTION_UPGRADED).set(true); - } - }) - .concatWith(Observable.defer(new Func0>() { - @Override - public Observable call() { - return wsUpEvt.handler.handle(new WebSocketConnection(wsConn)); - } - })) - .concatWith(Observable.create(new OnSubscribe() { - @Override - public void call(Subscriber sub) { - /* - * In this case, the client did not send a close frame but the server end processing - * is over, so we should send a close frame to indicate closure from server. - */ - if (wsConn.unsafeNettyChannel().isOpen()) { - wsConn.write(Observable.just(new CloseWebSocketFrame())) - .concatWith(wsConn.close()) - .unsafeSubscribe(sub); - } - } - })) - .unsafeSubscribe(subscriber); /*Unsafe as the subscriber is coming from the user.*/ - - } - - ctx.fireUserEventTriggered(evt); - } - - private static String configureResponseForWs(State state) { - - String acceptGuid; - - switch (state.getVersion()) { - case V07: - acceptGuid = WebSocketServerHandshaker07.WEBSOCKET_07_ACCEPT_GUID; - break; - case V08: - acceptGuid = WebSocketServerHandshaker08.WEBSOCKET_08_ACCEPT_GUID; - break; - case V13: - acceptGuid = WebSocketServerHandshaker13.WEBSOCKET_13_ACCEPT_GUID; - break; - default: - return "Unsupported web socket version: " + state.getVersion(); - } - - WebSocketFrameEncoder wsEncoder = new WebSocket13FrameEncoder(false /*servers should set this to false.*/); - WebSocketFrameDecoder wsDecoder = new WebSocket13FrameDecoder(true/*servers should set this to true.*/, - state.isAllowExtensions(), - state.getMaxFramePayloadLength(), true); - - final HttpServerResponse upgradeResponse = state.getUpgradeResponse(); - final MarkAwarePipeline pipeline = upgradeResponse.unsafeConnection().getResettableChannelPipeline(); - ChannelHandlerContext httpDecoderCtx = pipeline.context(HttpServerDecoder.getName()); - if (null == httpDecoderCtx) { - return "No HTTP decoder found, can not upgrade to WebSocket."; - } - ChannelHandlerContext httpEncoderCtx = pipeline.context(HttpServerEncoder.getName()); - if (null == httpEncoderCtx) { - return "No HTTP encoder found, can not upgrade to WebSocket."; - } - - pipeline.addAfter(httpDecoderCtx.name(), WsServerDecoder.getName(), wsDecoder); - pipeline.addBefore(httpEncoderCtx.name(), WsServerEncoder.getName(), wsEncoder); - - updateHandshakeHeaders(state, acceptGuid, upgradeResponse); - - return null; - } - - private static void updateHandshakeHeaders(State state, String acceptGuid, HttpServerResponse upgradeResponse) { - String acceptSeed = state.getSecWSkey() + acceptGuid; - byte[] sha1 = WsUtils.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); - String accept = WsUtils.base64(sha1); - - upgradeResponse.addHeader(SEC_WEBSOCKET_ACCEPT, accept); - upgradeResponse.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS); - upgradeResponse.addHeader(HttpHeaderNames.UPGRADE, WEBSOCKET); - upgradeResponse.addHeader(CONNECTION, HttpHeaderValues.UPGRADE); - - if (state.getRequestSubProtocols() != null) { - String selectedSubprotocol = WebSocketHandshaker.selectSubprotocol(state.getRequestSubProtocols(), - state.getSupportedSubProtocols()); - if (selectedSubprotocol != null) { - state.getUpgradeResponse().addHeader(SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); - } - } - } - - public static class WebSocket7To13UpgradeAcceptedEvent { - - private final Subscriber acceptUpgradeSubscriber; - private final WebSocketHandler handler; - private final State state; - private final HttpServerRequest request; - - WebSocket7To13UpgradeAcceptedEvent(Subscriber acceptUpgradeSubscriber, WebSocketHandler handler, - State state, HttpServerRequest request) { - this.acceptUpgradeSubscriber = acceptUpgradeSubscriber; - this.handler = handler; - this.state = state; - this.request = request; - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/TcpHandlerNames.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/TcpHandlerNames.java deleted file mode 100644 index e18f765..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/TcpHandlerNames.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp; - -/** - * A list of all handler names added for TCP. This is just to ensure consistency in naming. - */ -public enum TcpHandlerNames { - - ClientReadTimeoutHandler("client-read-timeout-handler"), - ; - - private final String name; - - TcpHandlerNames(String name) { - this.name = qualify(name); - } - - public String getName() { - return name; - } - - private static String qualify(String name) { - return "_rx_netty_" + name; - - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/ConnectionRequestImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/ConnectionRequestImpl.java deleted file mode 100644 index 7fdc6b4..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/ConnectionRequestImpl.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client; - -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.client.ConnectionProvider; -import io.reactivex.netty.client.ConnectionRequest; -import rx.Subscriber; - -final class ConnectionRequestImpl extends ConnectionRequest { - - ConnectionRequestImpl(final ConnectionProvider cp) { - super(new OnSubscribe>() { - @Override - public void call(final Subscriber> subscriber) { - cp.newConnectionRequest().unsafeSubscribe(subscriber); - } - }); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/InterceptingTcpClient.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/InterceptingTcpClient.java deleted file mode 100644 index 1541655..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/InterceptingTcpClient.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client; - -import io.reactivex.netty.client.ConnectionRequest; -import io.reactivex.netty.events.EventSource; -import io.reactivex.netty.protocol.tcp.client.events.TcpClientEventListener; - -public abstract class InterceptingTcpClient implements EventSource { - - /** - * Creates a new {@link ConnectionRequest} which should be subscribed to actually connect to the target server. - * - * @return A new {@link ConnectionRequest} which either can be subscribed directly or altered in various ways - * before subscription. - */ - public abstract ConnectionRequest createConnectionRequest(); - - /** - * Starts the process of adding interceptors to this client. Interceptors help in achieving various usecases of - * instrumenting and transforming connections. - * - * @return A new interceptor chain to add the various interceptors. - */ - public abstract TcpClientInterceptorChain intercept(); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/InterceptingTcpClientImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/InterceptingTcpClientImpl.java deleted file mode 100644 index a5786e2..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/InterceptingTcpClientImpl.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client; - -import io.reactivex.netty.client.ConnectionProvider; -import io.reactivex.netty.client.ConnectionRequest; -import io.reactivex.netty.protocol.tcp.client.events.TcpClientEventListener; -import io.reactivex.netty.protocol.tcp.client.events.TcpClientEventPublisher; -import rx.Subscription; - -public class InterceptingTcpClientImpl extends InterceptingTcpClient { - - private final ConnectionProvider cp; - private final TcpClientEventPublisher eventPublisher; - private final ConnectionRequest connectionRequest; - - public InterceptingTcpClientImpl(ConnectionProvider cp, TcpClientEventPublisher ep) { - this.cp = cp; - this.eventPublisher = ep; - connectionRequest = new ConnectionRequestImpl<>(this.cp); - - } - - @Override - public ConnectionRequest createConnectionRequest() { - return connectionRequest; - } - - @Override - public TcpClientInterceptorChain intercept() { - return new TcpClientInterceptorChainImpl<>(cp, eventPublisher); - } - - @Override - public Subscription subscribe(TcpClientEventListener listener) { - return eventPublisher.subscribe(listener); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/Interceptor.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/Interceptor.java deleted file mode 100644 index 93e42e4..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/Interceptor.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client; - -import io.reactivex.netty.client.ConnectionProvider; - -/** - * An interceptor that preserves the type of objects read and written to the connection. - * - * @param Type of objects read from the connection handled by this interceptor. - * @param Type of objects written to the connection handled by this interceptor. - */ -public interface Interceptor { - - /** - * Intercepts and optionally changes the passed {@code ConnectionProvider}. - * - * @param provider Provider to intercept. - * - * @return Provider to use after this transformation. - */ - ConnectionProvider intercept(ConnectionProvider provider); - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TcpClient.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TcpClient.java deleted file mode 100644 index 5908ac7..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TcpClient.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslHandler; -import io.netty.util.concurrent.EventExecutorGroup; -import io.reactivex.netty.client.ChannelProviderFactory; -import io.reactivex.netty.client.ConnectionProviderFactory; -import io.reactivex.netty.client.Host; -import io.reactivex.netty.ssl.SslCodec; -import rx.Observable; -import rx.functions.Action1; -import rx.functions.Func0; -import rx.functions.Func1; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.TrustManagerFactory; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.concurrent.TimeUnit; - -/** - * A TCP client for creating TCP connections. - * - *

Immutability

- * An instance of this client is immutable and all mutations produce a new client instance. For this reason it is - * recommended that the mutations are done during client creation and not during connection creation to avoid repeated - * object creation overhead. - * - * @param The type of objects written to this client. - * @param The type of objects read from this client. - */ -public abstract class TcpClient extends InterceptingTcpClient { - - /** - * Creates a new client instances, inheriting all configurations from this client and adding a - * {@link ChannelOption} for the connections created by the newly created client instance. - * - * @param option Option to add. - * @param value Value for the option. - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient channelOption(ChannelOption option, T value); - - /** - * Creates a new client instances, inheriting all configurations from this client and enables read timeout for all - * the connection created by this client. - * - * @param timeOut Read timeout duration. - * @param timeUnit Read timeout time unit. - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient readTimeOut(int timeOut, TimeUnit timeUnit); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this client. The specified - * handler is added at the first position of the pipeline as specified by - * {@link ChannelPipeline#addFirst(String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param name Name of the handler. - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient addChannelHandlerFirst(String name, Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this client. The specified - * handler is added at the first position of the pipeline as specified by - * {@link ChannelPipeline#addFirst(EventExecutorGroup, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} - * methods - * @param name the name of the handler to append - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient addChannelHandlerFirst(EventExecutorGroup group, String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this client. The specified - * handler is added at the last position of the pipeline as specified by - * {@link ChannelPipeline#addLast(String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param name Name of the handler. - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient addChannelHandlerLast(String name, Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this client. The specified - * handler is added at the last position of the pipeline as specified by - * {@link ChannelPipeline#addLast(EventExecutorGroup, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} - * methods - * @param name the name of the handler to append - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient addChannelHandlerLast(EventExecutorGroup group, String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this client. The specified - * handler is added before an existing handler with the passed {@code baseName} in the pipeline as specified by - * {@link ChannelPipeline#addBefore(String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param baseName the name of the existing handler - * @param name Name of the handler. - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient addChannelHandlerBefore(String baseName, String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this client. The specified - * handler is added before an existing handler with the passed {@code baseName} in the pipeline as specified by - * {@link ChannelPipeline#addBefore(EventExecutorGroup, String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} - * methods - * @param baseName the name of the existing handler - * @param name the name of the handler to append - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient addChannelHandlerBefore(EventExecutorGroup group, String baseName, - String name, Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this client. The specified - * handler is added after an existing handler with the passed {@code baseName} in the pipeline as specified by - * {@link ChannelPipeline#addAfter(String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param baseName the name of the existing handler - * @param name Name of the handler. - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient addChannelHandlerAfter(String baseName, String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this client. The specified - * handler is added after an existing handler with the passed {@code baseName} in the pipeline as specified by - * {@link ChannelPipeline#addAfter(EventExecutorGroup, String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} - * methods - * @param baseName the name of the existing handler - * @param name the name of the handler to append - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient addChannelHandlerAfter(EventExecutorGroup group, String baseName, - String name, Func0 handlerFactory); - - /** - * Creates a new client instances, inheriting all configurations from this client and using the passed - * action to configure all the connections created by the newly created client instance. - * - * @param pipelineConfigurator Action to configure {@link ChannelPipeline}. - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient pipelineConfigurator(Action1 pipelineConfigurator); - - /** - * Creates a new client instances, inheriting all configurations from this client and enabling wire logging at the - * passed level for the newly created client instance. - * - * @param wireLoggingLevel Logging level at which the wire logs will be logged. The wire logging will only be done if - * logging is enabled at this level for {@link LoggingHandler} - * - * @return A new {@link TcpClient} instance. - * - * @deprecated Use {@link #enableWireLogging(String, LogLevel)} instead. - */ - @Deprecated - public abstract TcpClient enableWireLogging(LogLevel wireLoggingLevel); - - /** - * Creates a new client instances, inheriting all configurations from this client and enabling wire logging at the - * passed level for the newly created client instance. - * - * @param name Name of the logger that can be used to control the logging dynamically. - * @param wireLoggingLevel Logging level at which the wire logs will be logged. The wire logging will only be done if - * logging is enabled at this level for {@link LoggingHandler} - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient enableWireLogging(String name, LogLevel wireLoggingLevel); - - /** - * Creates a new client instance, inheriting all configurations from this client and using the passed - * {@code sslEngineFactory} for all secured connections created by the newly created client instance. - * - * If the {@link SSLEngine} instance can be statically, created, {@link #secure(SSLEngine)} can be used. - * - * @param sslEngineFactory Factory for all secured connections created by the newly created client instance. - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient secure(Func1 sslEngineFactory); - - /** - * Creates a new client instance, inheriting all configurations from this client and using the passed - * {@code sslEngine} for all secured connections created by the newly created client instance. - * - * If the {@link SSLEngine} instance can not be statically, created, {@link #secure(Func1)} )} can be used. - * - * @param sslEngine {@link SSLEngine} for all secured connections created by the newly created client instance. - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient secure(SSLEngine sslEngine); - - /** - * Creates a new client instance, inheriting all configurations from this client and using the passed - * {@code sslCodec} for all secured connections created by the newly created client instance. - * - * This is required only when the {@link SslHandler} used by {@link SslCodec} is to be modified before adding to - * the {@link ChannelPipeline}. For most of the cases, {@link #secure(Func1)} or {@link #secure(SSLEngine)} will be - * enough. - * - * @param sslCodec {@link SslCodec} for all secured connections created by the newly created client instance. - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient secure(SslCodec sslCodec); - - /** - * Creates a new client instance, inheriting all configurations from this client and using a trust-all - * {@link TrustManagerFactory}for all secured connections created by the newly created client instance. - * - * This is only for testing and should not be used for real production clients. - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient unsafeSecure(); - - /** - * Creates a new client instance, inheriting all configurations from this client and using the passed - * {@code providerFactory}. - * - * @param providerFactory Channel provider factory. - * - * @return A new {@link TcpClient} instance. - */ - public abstract TcpClient channelProvider(ChannelProviderFactory providerFactory); - - /** - * Creates a new TCP client instance with the passed address of the target server. - * - * @param host Hostname for the target server. - * @param port Port for the target server. - * - * @return A new {@code TcpClient} instance. - */ - public static TcpClient newClient(String host, int port) { - return newClient(new InetSocketAddress(host, port)); - } - - /** - * Creates a new TCP client instance with the passed address of the target server. - * - * @param serverAddress Socket address for the target server. - * - * @return A new {@code TcpClient} instance. - */ - public static TcpClient newClient(SocketAddress serverAddress) { - return TcpClientImpl.create(serverAddress); - } - - /** - * Creates a new TCP client instance with the passed address of the target server. - * - * @param serverAddress Socket address for the target server. - * @param eventLoopGroup Eventloop group for the client. - * @param channelClass Channel class for the client. - * - * @return A new {@code TcpClient} instance. - */ - public static TcpClient newClient(SocketAddress serverAddress, EventLoopGroup eventLoopGroup, - Class channelClass) { - return TcpClientImpl.create(serverAddress, eventLoopGroup, channelClass); - } - - /** - * Creates a new TCP client instance using the supplied connection provider. - * - * @param providerFactory {@link ConnectionProviderFactory} for the client. - * @param hostStream Stream of hosts for the client. - * - * @return A new {@code TcpClient} instance. - */ - public static TcpClient newClient(ConnectionProviderFactory providerFactory, - Observable hostStream) { - return TcpClientImpl.create(providerFactory, hostStream); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TcpClientImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TcpClientImpl.java deleted file mode 100644 index e73e5be..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TcpClientImpl.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.handler.logging.LogLevel; -import io.netty.util.concurrent.EventExecutorGroup; -import io.reactivex.netty.HandlerNames; -import io.reactivex.netty.channel.ChannelSubscriberEvent; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.ConnectionImpl; -import io.reactivex.netty.channel.DetachedChannelPipeline; -import io.reactivex.netty.client.ChannelProvider; -import io.reactivex.netty.client.ChannelProviderFactory; -import io.reactivex.netty.client.ClientState; -import io.reactivex.netty.client.ConnectionProvider; -import io.reactivex.netty.client.ConnectionProviderFactory; -import io.reactivex.netty.client.ConnectionRequest; -import io.reactivex.netty.client.Host; -import io.reactivex.netty.client.HostConnector; -import io.reactivex.netty.client.internal.SingleHostConnectionProvider; -import io.reactivex.netty.events.EventSource; -import io.reactivex.netty.internal.InternalReadTimeoutHandler; -import io.reactivex.netty.protocol.tcp.client.events.TcpClientEventListener; -import io.reactivex.netty.protocol.tcp.client.events.TcpClientEventPublisher; -import io.reactivex.netty.protocol.tcp.client.internal.TcpChannelProviderFactory; -import io.reactivex.netty.ssl.SslCodec; -import rx.Observable; -import rx.Observable.OnSubscribe; -import rx.Subscriber; -import rx.Subscription; -import rx.functions.Action0; -import rx.functions.Action1; -import rx.functions.Func0; -import rx.functions.Func1; -import rx.subscriptions.Subscriptions; - -import javax.net.ssl.SSLEngine; -import java.net.SocketAddress; -import java.util.concurrent.TimeUnit; - -public final class TcpClientImpl extends TcpClient { - - private final ClientState state; - private final TcpClientEventPublisher eventPublisher; - private final InterceptingTcpClient interceptingTcpClient; - private ConnectionRequestImpl requestSetLazily; - - private TcpClientImpl(ClientState state, TcpClientEventPublisher eventPublisher, - InterceptingTcpClient interceptingTcpClient) { - this.state = state; - this.eventPublisher = eventPublisher; - this.interceptingTcpClient = interceptingTcpClient; - } - - @Override - public ConnectionRequest createConnectionRequest() { - return requestSetLazily; - } - - @Override - public TcpClient channelOption(ChannelOption option, T value) { - return copy(state.channelOption(option, value), eventPublisher); - } - - @Override - public TcpClient readTimeOut(final int timeOut, final TimeUnit timeUnit) { - return addChannelHandlerFirst(HandlerNames.ClientReadTimeoutHandler.getName(), new Func0() { - @Override - public ChannelHandler call() { - return new InternalReadTimeoutHandler(timeOut, timeUnit); - } - }); - } - - @Override - public TcpClient addChannelHandlerFirst(String name, Func0 handlerFactory) { - return copy(state.addChannelHandlerFirst(name, handlerFactory), eventPublisher); - } - - @Override - public TcpClient addChannelHandlerFirst(EventExecutorGroup group, String name, - Func0 handlerFactory) { - return copy(state.addChannelHandlerFirst(group, name, handlerFactory), eventPublisher); - } - - @Override - public TcpClient addChannelHandlerLast(String name, Func0 handlerFactory) { - return copy(state.addChannelHandlerLast(name, handlerFactory), eventPublisher); - } - - @Override - public TcpClient addChannelHandlerLast(EventExecutorGroup group, String name, - Func0 handlerFactory) { - return copy(state.addChannelHandlerLast(group, name, handlerFactory), eventPublisher); - } - - @Override - public TcpClient addChannelHandlerBefore(String baseName, String name, - Func0 handlerFactory) { - return copy(state.addChannelHandlerBefore(baseName, name, handlerFactory), eventPublisher); - } - - @Override - public TcpClient addChannelHandlerBefore(EventExecutorGroup group, String baseName, String name, - Func0 handlerFactory) { - return copy(state.addChannelHandlerBefore(group, baseName, name, handlerFactory), eventPublisher); - } - - @Override - public TcpClient addChannelHandlerAfter(String baseName, String name, - Func0 handlerFactory) { - return copy(state.addChannelHandlerAfter(baseName, name, handlerFactory), eventPublisher); - } - - @Override - public TcpClient addChannelHandlerAfter(EventExecutorGroup group, String baseName, String name, - Func0 handlerFactory) { - return copy(state.addChannelHandlerAfter(group, baseName, name, handlerFactory), eventPublisher); - } - - @Override - public TcpClient pipelineConfigurator(Action1 pipelineConfigurator) { - return copy(state.pipelineConfigurator(pipelineConfigurator), eventPublisher); - } - - @Override - @Deprecated - public TcpClient enableWireLogging(LogLevel wireLoggingLevel) { - return copy(state.enableWireLogging(wireLoggingLevel), eventPublisher); - } - - @Override - public TcpClient enableWireLogging(String name, LogLevel wireLoggingLevel) { - return copy(state.enableWireLogging(name, wireLoggingLevel), eventPublisher); - } - - @Override - public TcpClient secure(Func1 sslEngineFactory) { - return copy(state.secure(sslEngineFactory), eventPublisher); - } - - @Override - public TcpClient secure(SSLEngine sslEngine) { - return copy(state.secure(sslEngine), eventPublisher); - } - - @Override - public TcpClient secure(SslCodec sslCodec) { - return copy(state.secure(sslCodec), eventPublisher); - } - - @Override - public TcpClient unsafeSecure() { - return copy(state.unsafeSecure(), eventPublisher); - } - - @Override - public TcpClient channelProvider(ChannelProviderFactory providerFactory) { - return copy(state.channelProviderFactory(providerFactory), eventPublisher); - } - - @Override - public Subscription subscribe(TcpClientEventListener listener) { - return interceptingTcpClient.subscribe(listener); - } - - @Override - public TcpClientInterceptorChain intercept() { - return interceptingTcpClient.intercept(); - } - - /*Visible for testing*/ ClientState getClientState() { - return state; - } - - public static TcpClientImpl create(SocketAddress socketAddress) { - return create(socketAddress, ClientState.defaultEventloopGroup(), ClientState.defaultSocketChannelClass()); - } - - public static TcpClientImpl create(SocketAddress socketAddress, EventLoopGroup eventLoopGroup, - Class channelClass) { - final Host host = new Host(socketAddress); - ConnectionProviderFactory factory = new ConnectionProviderFactory() { - @Override - public ConnectionProvider newProvider(Observable> hosts) { - return new SingleHostConnectionProvider<>(hosts); - } - }; - Observable hostStream = Observable.just(host); - ClientState state = ClientState.create(factory, hostStream, eventLoopGroup, channelClass); - final TcpClientEventPublisher eventPublisher = new TcpClientEventPublisher(); - return _create(state, eventPublisher); - } - - public static TcpClientImpl create(ConnectionProviderFactory factory, - Observable hostStream) { - ClientState state = ClientState.create(factory, hostStream); - final TcpClientEventPublisher eventPublisher = new TcpClientEventPublisher(); - return _create(state, eventPublisher); - } - - private static TcpClientImpl copy(final ClientState state, - TcpClientEventPublisher eventPublisher) { - return _create(state, eventPublisher); - } - - /*Visible for testing*/ static TcpClientImpl _create(ClientState state, - TcpClientEventPublisher eventPublisher) { - DetachedChannelPipeline channelPipeline = state.unsafeDetachedPipeline(); - state = state.channelProviderFactory(new TcpChannelProviderFactory(channelPipeline, - state.getChannelProviderFactory())); - - HostConnectorFactory hostConnectorFactory = new HostConnectorFactory<>(state, eventPublisher); - - ConnectionProvider cp = state.getFactory() - .newProvider(state.getHostStream().map(hostConnectorFactory)); - - InterceptingTcpClient interceptingTcpClient = new InterceptingTcpClientImpl<>(cp, eventPublisher); - TcpClientImpl client = new TcpClientImpl<>(state, eventPublisher, interceptingTcpClient); - client.requestSetLazily = new ConnectionRequestImpl<>(cp); - return client; - } - - private static class HostConnectorFactory implements Func1> { - - private final ChannelProviderFactory channelProviderFactory; - private final TcpClientEventPublisher clientEventPublisher; - private final ClientState state; - - public HostConnectorFactory(ClientState state, TcpClientEventPublisher clientEventPublisher) { - this.state = state; - channelProviderFactory = state.getChannelProviderFactory(); - this.clientEventPublisher = clientEventPublisher; - } - - @Override - public HostConnector call(final Host host) { - TcpClientEventPublisher hostEventPublisher = new TcpClientEventPublisher(); - @SuppressWarnings({"unchecked", "rawtypes"}) - EventSource eventSource = hostEventPublisher; - hostEventPublisher.subscribe(clientEventPublisher); - @SuppressWarnings("unchecked") - ChannelProvider channelProvider = channelProviderFactory.newProvider(host, eventSource, hostEventPublisher, - hostEventPublisher); - return new HostConnector<>(host, new TerminalConnectionProvider<>(hostEventPublisher, host, - channelProvider, state), - hostEventPublisher, hostEventPublisher, hostEventPublisher); - } - } - - private static class TerminalConnectionProvider implements ConnectionProvider { - - private final Host host; - private final Bootstrap bootstrap; - private final ChannelProvider channelProvider; - - public TerminalConnectionProvider(TcpClientEventPublisher hostEventPublisher, - Host host, ChannelProvider channelProvider, ClientState state) { - this.host = host; - this.channelProvider = channelProvider; - bootstrap = state.newBootstrap(hostEventPublisher, hostEventPublisher); - } - - @Override - public Observable> newConnectionRequest() { - return channelProvider.newChannel(Observable.create(new OnSubscribe() { - @Override - public void call(final Subscriber s) { - final ChannelFuture cf = bootstrap.connect(host.getHost()); - s.add(Subscriptions.create(new Action0() { - @Override - public void call() { - if (null != cf && !cf.isDone()) { - cf.cancel(false); - } - } - })); - cf.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(final ChannelFuture future) throws Exception { - if (!future.isSuccess()) { - s.onError(future.cause()); - } else { - s.onNext(cf.channel()); - s.onCompleted(); - } - } - }); - } - })).switchMap(new Func1>() { - @Override - public Observable call(final Channel channel) { - - /* - * If channel is unregistered, all handlers are removed and hence the event will not flow through - * to the handler for the subscriber to be notified. - * So, here the channel is directly passed through the chain if the channel isn't registered. - */ - if (channel.eventLoop().inEventLoop()) { - if (channel.isRegistered()) { - return Observable.create(new OnSubscribe() { - @Override - public void call(Subscriber subscriber) { - channel.pipeline().fireUserEventTriggered(new ChannelSubscriberEvent<>(subscriber)); - } - }); - } else { - return Observable.just(channel); - } - } else { - return Observable.create(new OnSubscribe() { - @Override - public void call(final Subscriber subscriber) { - channel.eventLoop().execute(new Runnable() { - @Override - public void run() { - if (channel.isRegistered()) { - channel.pipeline() - .fireUserEventTriggered(new ChannelSubscriberEvent<>(subscriber)); - } else { - subscriber.onNext(channel); - subscriber.onCompleted(); - } - } - }); - } - }); - } - } - }).map(new Func1>() { - @Override - public Connection call(Channel channel) { - return ConnectionImpl.fromChannel(channel); - } - }); - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TcpClientInterceptorChain.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TcpClientInterceptorChain.java deleted file mode 100644 index 1790336..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TcpClientInterceptorChain.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client; - -/** - * Interceptor chain for {@link TcpClient}, obtained via {@link TcpClient#intercept()}.

- * - * Multiple interceptors can be added to this chain by using the various {@code next*()} methods available, before - * calling {@link #finish()} that returns a new {@link TcpClient} which inherits all the configuration from the parent - * client (from which this chain was created) and adds these interceptors. - * - *

Order of execution

- * - * Interceptors are executed in the order in which they are added. - * - * @param The type of objects written to the client created by this chain. - * @param The type of objects read from the client created by this chain. - */ -public interface TcpClientInterceptorChain { - - /** - * Adds a simple interceptor that does not change the type of objects read/written to a connection. - * - * @param interceptor Interceptor to add. - * - * @return {@code this} - */ - TcpClientInterceptorChain next(Interceptor interceptor); - - /** - * Adds an interceptor that changes the type of objects read from the connections created by the client provided by - * this chain. - * - * @param interceptor Interceptor to add. - * - * @return A new chain instance. - */ - TcpClientInterceptorChain nextWithReadTransform(TransformingInterceptor interceptor); - - /** - * Adds an interceptor that changes the type of objects written to the connections created by the client provided by - * this chain. - * - * @param interceptor Interceptor to add. - * - * @return A new chain instance. - */ - TcpClientInterceptorChain nextWithWriteTransform(TransformingInterceptor interceptor); - - /** - * Adds an interceptor that changes the type of objects read and written to the connections created by the client - * provided by this chain. - * - * @param interceptor Interceptor to add. - * - * @return A new chain instance. - */ - TcpClientInterceptorChain nextWithTransform(TransformingInterceptor interceptor); - - /** - * Finish the addition of interceptors and create a new client instance. - * - * @return New client instance which inherits all the configuration from the parent client - * (from which this chain was created) and adds these interceptors. - */ - InterceptingTcpClient finish(); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TcpClientInterceptorChainImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TcpClientInterceptorChainImpl.java deleted file mode 100644 index 8fcd7d8..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TcpClientInterceptorChainImpl.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client; - -import io.reactivex.netty.client.ConnectionProvider; -import io.reactivex.netty.protocol.tcp.client.events.TcpClientEventPublisher; - -public class TcpClientInterceptorChainImpl implements TcpClientInterceptorChain { - - private ConnectionProvider connectionProvider; - private final TcpClientEventPublisher eventPublisher; - - public TcpClientInterceptorChainImpl(ConnectionProvider cp, TcpClientEventPublisher ep) { - connectionProvider = cp; - this.eventPublisher = ep; - } - - @Override - public TcpClientInterceptorChain next(Interceptor i) { - connectionProvider = i.intercept(connectionProvider); - return this; - } - - @Override - public TcpClientInterceptorChain nextWithReadTransform(TransformingInterceptor i) { - return new TcpClientInterceptorChainImpl<>(i.intercept(connectionProvider), eventPublisher); - } - - @Override - public TcpClientInterceptorChain nextWithWriteTransform(TransformingInterceptor i) { - return new TcpClientInterceptorChainImpl<>(i.intercept(connectionProvider), eventPublisher); - } - - @Override - public TcpClientInterceptorChain nextWithTransform(TransformingInterceptor i) { - return new TcpClientInterceptorChainImpl<>(i.intercept(connectionProvider), eventPublisher); - } - - @Override - public InterceptingTcpClient finish() { - return new InterceptingTcpClientImpl<>(connectionProvider, eventPublisher); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TransformingInterceptor.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TransformingInterceptor.java deleted file mode 100644 index 47967fa..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/TransformingInterceptor.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client; - -import io.reactivex.netty.client.ConnectionProvider; - -/** - * An interceptor that changes the type of objects read and written to the connection. - * - * @param Type of objects read from the connection before applying this interceptor. - * @param Type of objects written to the connection before applying this interceptor. - * @param Type of objects read from the connection after applying this interceptor. - * @param Type of objects written to the connection after applying this interceptor. - */ -public interface TransformingInterceptor { - - /** - * Intercepts and changes the passed {@code ConnectionProvider}. - * - * @param provider Provider to intercept. - * - * @return Provider to use after this transformation. - */ - ConnectionProvider intercept(ConnectionProvider provider); - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/events/SafeTcpClientEventListener.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/events/SafeTcpClientEventListener.java deleted file mode 100644 index 5b7e94e..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/events/SafeTcpClientEventListener.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client.events; - -import io.reactivex.netty.events.internal.SafeEventListener; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -final class SafeTcpClientEventListener extends TcpClientEventListener implements SafeEventListener { - - private final TcpClientEventListener delegate; - private final AtomicBoolean completed = new AtomicBoolean(); - - public SafeTcpClientEventListener(TcpClientEventListener delegate) { - this.delegate = delegate; - } - - @Override - public void onCompleted() { - if (completed.compareAndSet(false, true)) { - delegate.onCompleted(); - } - } - - @Override - public void onConnectStart() { - if (!completed.get()) { - delegate.onConnectStart(); - } - } - - @Override - public void onConnectSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onConnectSuccess(duration, timeUnit); - } - } - - @Override - public void onConnectFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - if (!completed.get()) { - delegate.onConnectFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onPoolReleaseStart() { - if (!completed.get()) { - delegate.onPoolReleaseStart(); - } - } - - @Override - public void onPoolReleaseSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onPoolReleaseSuccess(duration, timeUnit); - } - } - - @Override - public void onPoolReleaseFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - if (!completed.get()) { - delegate.onPoolReleaseFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onPooledConnectionEviction() { - if (!completed.get()) { - delegate.onPooledConnectionEviction(); - } - } - - @Override - public void onPooledConnectionReuse() { - if (!completed.get()) { - delegate.onPooledConnectionReuse(); - } - } - - @Override - public void onPoolAcquireStart() { - if (!completed.get()) { - delegate.onPoolAcquireStart(); - } - } - - @Override - public void onPoolAcquireSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onPoolAcquireSuccess(duration, timeUnit); - } - } - - @Override - public void onPoolAcquireFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - if (!completed.get()) { - delegate.onPoolAcquireFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onByteRead(long bytesRead) { - if (!completed.get()) { - delegate.onByteRead(bytesRead); - } - } - - @Override - public void onByteWritten(long bytesWritten) { - if (!completed.get()) { - delegate.onByteWritten(bytesWritten); - } - } - - @Override - public void onFlushStart() { - if (!completed.get()) { - delegate.onFlushStart(); - } - } - - @Override - public void onFlushComplete(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onFlushComplete(duration, timeUnit); - } - } - - @Override - public void onWriteStart() { - if (!completed.get()) { - delegate.onWriteStart(); - } - } - - @Override - public void onWriteSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onWriteSuccess(duration, timeUnit); - } - } - - @Override - public void onWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - if (!completed.get()) { - delegate.onWriteFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onConnectionCloseStart() { - if (!completed.get()) { - delegate.onConnectionCloseStart(); - } - } - - @Override - public void onConnectionCloseSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onConnectionCloseSuccess(duration, timeUnit); - } - } - - @Override - public void onConnectionCloseFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - if (!completed.get()) { - delegate.onConnectionCloseFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onCustomEvent(Object event) { - if (!completed.get()) { - delegate.onCustomEvent(event); - } - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onCustomEvent(event, duration, timeUnit); - } - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { - if (!completed.get()) { - delegate.onCustomEvent(event, duration, timeUnit, throwable); - } - } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { - if (!completed.get()) { - delegate.onCustomEvent(event, throwable); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof SafeTcpClientEventListener)) { - return false; - } - - SafeTcpClientEventListener that = (SafeTcpClientEventListener) o; - - return !(delegate != null? !delegate.equals(that.delegate) : that.delegate != null); - - } - - @Override - public int hashCode() { - return delegate != null? delegate.hashCode() : 0; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/events/TcpClientEventListener.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/events/TcpClientEventListener.java deleted file mode 100644 index 0392b0f..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/events/TcpClientEventListener.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client.events; - -import io.reactivex.netty.client.events.ClientEventListener; -import io.reactivex.netty.protocol.tcp.client.TcpClient; - -/** - * A listener for all events published by {@link TcpClient} - */ -public abstract class TcpClientEventListener extends ClientEventListener { - -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/events/TcpClientEventPublisher.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/events/TcpClientEventPublisher.java deleted file mode 100644 index 390c56d..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/events/TcpClientEventPublisher.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client.events; - -import io.reactivex.netty.channel.events.ConnectionEventPublisher; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.events.EventSource; -import io.reactivex.netty.events.ListenersHolder; -import io.reactivex.netty.events.internal.SafeEventListener; -import rx.Subscription; -import rx.functions.Action1; -import rx.functions.Action3; -import rx.functions.Action4; -import rx.subscriptions.CompositeSubscription; - -import java.util.concurrent.TimeUnit; - -public final class TcpClientEventPublisher extends TcpClientEventListener - implements EventSource, EventPublisher { - - public static final Action1 CONN_START_ACTION = new Action1() { - @Override - public void call(TcpClientEventListener l) { - l.onConnectStart(); - } - }; - - public static final Action3 CONN_SUCCESS_ACTION = - new Action3() { - @Override - public void call(TcpClientEventListener l, Long duration, TimeUnit timeUnit) { - l.onConnectSuccess(duration, timeUnit); - } - }; - - public static final Action4 CONN_FAILED_ACTION = - new Action4() { - @Override - public void call(TcpClientEventListener l, Long duration, TimeUnit timeUnit, Throwable t) { - l.onConnectFailed(duration, timeUnit, t); - } - }; - - public static final Action1 EVICTION_ACTION = new Action1() { - @Override - public void call(TcpClientEventListener l) { - l.onPooledConnectionEviction(); - } - }; - - public static final Action1 REUSE_ACTION = new Action1() { - @Override - public void call(TcpClientEventListener l) { - l.onPooledConnectionReuse(); - } - }; - - public static final Action1 ACQUIRE_START_ACTION = new Action1() { - @Override - public void call(TcpClientEventListener l) { - l.onPoolAcquireStart(); - } - }; - - public static final Action3 ACQUIRE_SUCCESS_ACTION = - new Action3() { - @Override - public void call(TcpClientEventListener l, Long duration, TimeUnit timeUnit) { - l.onPoolAcquireSuccess(duration, timeUnit); - } - }; - - public static final Action4 ACQUIRE_FAILED_ACTION = - new Action4() { - @Override - public void call(TcpClientEventListener l, Long duration, TimeUnit timeUnit, Throwable t) { - l.onPoolAcquireFailed(duration, timeUnit, t); - } - }; - - public static final Action1 RELEASE_START_ACTION = new Action1() { - @Override - public void call(TcpClientEventListener l) { - l.onPoolReleaseStart(); - } - }; - - public static final Action3 RELEASE_SUCCESS_ACTION = - new Action3() { - @Override - public void call(TcpClientEventListener l, Long duration, TimeUnit timeUnit) { - l.onPoolReleaseSuccess(duration, timeUnit); - } - }; - - public static final Action4 RELEASE_FAILED_ACTION = - new Action4() { - @Override - public void call(TcpClientEventListener l, Long duration, TimeUnit timeUnit, Throwable t) { - l.onPoolReleaseFailed(duration, timeUnit, t); - } - }; - - private final ListenersHolder listeners; - private final ConnectionEventPublisher connDelegate; - - public TcpClientEventPublisher() { - listeners = new ListenersHolder<>(); - connDelegate = new ConnectionEventPublisher<>(); - } - - public TcpClientEventPublisher(TcpClientEventPublisher toCopy) { - listeners = toCopy.listeners.copy(); - connDelegate = toCopy.connDelegate.copy(); - } - - @Override - public void onConnectStart() { - listeners.invokeListeners(CONN_START_ACTION); - } - - @Override - public void onConnectSuccess(final long duration, final TimeUnit timeUnit) { - listeners.invokeListeners(CONN_SUCCESS_ACTION, duration, timeUnit); - } - - @Override - public void onConnectFailed(final long duration, final TimeUnit timeUnit, final Throwable throwable) { - listeners.invokeListeners(CONN_FAILED_ACTION, duration, timeUnit, throwable); - } - - @Override - public void onPoolReleaseStart() { - listeners.invokeListeners(RELEASE_START_ACTION); - } - - @Override - public void onPoolReleaseSuccess(final long duration, final TimeUnit timeUnit) { - listeners.invokeListeners(RELEASE_SUCCESS_ACTION, duration, timeUnit); - } - - @Override - public void onPoolReleaseFailed(final long duration, final TimeUnit timeUnit, final Throwable throwable) { - listeners.invokeListeners(RELEASE_FAILED_ACTION, duration, timeUnit, throwable); - } - - @Override - public void onPooledConnectionEviction() { - listeners.invokeListeners(EVICTION_ACTION); - } - - @Override - public void onPooledConnectionReuse() { - listeners.invokeListeners(REUSE_ACTION); - } - - @Override - public void onPoolAcquireStart() { - listeners.invokeListeners(ACQUIRE_START_ACTION); - } - - @Override - public void onPoolAcquireSuccess(final long duration, final TimeUnit timeUnit) { - listeners.invokeListeners(ACQUIRE_SUCCESS_ACTION, duration, timeUnit); - } - - @Override - public void onPoolAcquireFailed(final long duration, final TimeUnit timeUnit, final Throwable throwable) { - listeners.invokeListeners(ACQUIRE_FAILED_ACTION, duration, timeUnit, throwable); - } - - @Override - public void onByteRead(long bytesRead) { - connDelegate.onByteRead(bytesRead); - } - - @Override - public void onByteWritten(long bytesWritten) { - connDelegate.onByteWritten(bytesWritten); - } - - @Override - public void onFlushStart() { - connDelegate.onFlushStart(); - } - - @Override - public void onFlushComplete(long duration, TimeUnit timeUnit) { - connDelegate.onFlushComplete(duration, timeUnit); - } - - @Override - public void onWriteStart() { - connDelegate.onWriteStart(); - } - - @Override - public void onWriteSuccess(long duration, TimeUnit timeUnit) { - connDelegate.onWriteSuccess(duration, timeUnit); - } - - @Override - public void onWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - connDelegate.onWriteFailed(duration, timeUnit, throwable); - } - - @Override - public void onConnectionCloseStart() { - connDelegate.onConnectionCloseStart(); - } - - @Override - public void onConnectionCloseSuccess(long duration, TimeUnit timeUnit) { - connDelegate.onConnectionCloseSuccess(duration, timeUnit); - } - - @Override - public void onConnectionCloseFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - connDelegate.onConnectionCloseFailed(duration, timeUnit, throwable); - } - - @Override - public void onCustomEvent(Object event) { - connDelegate.onCustomEvent(event); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { - connDelegate.onCustomEvent(event, duration, timeUnit); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { - connDelegate.onCustomEvent(event, duration, timeUnit, throwable); - } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { - connDelegate.onCustomEvent(event, throwable); - } - - @Override - public Subscription subscribe(TcpClientEventListener listener) { - if (!SafeEventListener.class.isAssignableFrom(listener.getClass())) { - listener = new SafeTcpClientEventListener(listener); - } - - CompositeSubscription cs = new CompositeSubscription(); - cs.add(listeners.subscribe(listener)); - cs.add(connDelegate.subscribe(listener)); - return cs; - } - - @Override - public boolean publishingEnabled() { - return listeners.publishingEnabled(); - } - - public TcpClientEventPublisher copy() { - return new TcpClientEventPublisher(this); - } - - /*Visible for testing*/ ListenersHolder getListeners() { - return listeners; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/internal/TcpChannelProvider.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/internal/TcpChannelProvider.java deleted file mode 100644 index b79b74b..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/internal/TcpChannelProvider.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client.internal; - -import io.netty.channel.Channel; -import io.reactivex.netty.channel.DetachedChannelPipeline; -import io.reactivex.netty.client.ChannelProvider; -import io.reactivex.netty.client.events.ClientEventListener; -import io.reactivex.netty.events.EventAttributeKeys; -import io.reactivex.netty.events.EventPublisher; -import rx.Observable; -import rx.functions.Func1; - -public class TcpChannelProvider implements ChannelProvider { - - private final DetachedChannelPipeline channelPipeline; - private final ChannelProvider delegate; - private final EventPublisher publisher; - private final ClientEventListener hostEventPublisher; - - public TcpChannelProvider(DetachedChannelPipeline channelPipeline, ChannelProvider delegate, - EventPublisher publisher, ClientEventListener hostEventPublisher) { - this.channelPipeline = channelPipeline; - this.delegate = delegate; - this.publisher = publisher; - this.hostEventPublisher = hostEventPublisher; - } - - @Override - public Observable newChannel(Observable input) { - return delegate.newChannel(input) - .map(new Func1() { - @Override - public Channel call(Channel channel) { - channel.attr(EventAttributeKeys.EVENT_PUBLISHER).set(publisher); - channel.attr(EventAttributeKeys.CLIENT_EVENT_LISTENER).set(hostEventPublisher); - channel.attr(EventAttributeKeys.CONNECTION_EVENT_LISTENER).set(hostEventPublisher); - channelPipeline.addToChannel(channel); - return channel; - } - }); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/internal/TcpChannelProviderFactory.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/internal/TcpChannelProviderFactory.java deleted file mode 100644 index c66ecf9..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/client/internal/TcpChannelProviderFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client.internal; - -import io.reactivex.netty.channel.DetachedChannelPipeline; -import io.reactivex.netty.client.ChannelProvider; -import io.reactivex.netty.client.ChannelProviderFactory; -import io.reactivex.netty.client.Host; -import io.reactivex.netty.client.events.ClientEventListener; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.events.EventSource; - -public class TcpChannelProviderFactory implements ChannelProviderFactory { - - private final DetachedChannelPipeline channelPipeline; - private final ChannelProviderFactory delegate; - - public TcpChannelProviderFactory(DetachedChannelPipeline channelPipeline, ChannelProviderFactory delegate) { - this.channelPipeline = channelPipeline; - this.delegate = delegate instanceof TcpChannelProviderFactory ? ((TcpChannelProviderFactory) delegate).delegate - : delegate; - } - - @Override - public ChannelProvider newProvider(Host host, EventSource hostEventSource, - EventPublisher publisher, ClientEventListener hostEventPublisher) { - ChannelProvider delegate = this.delegate.newProvider(host, hostEventSource, publisher, hostEventPublisher); - return new TcpChannelProvider(channelPipeline, delegate, publisher, hostEventPublisher); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/ConnectionHandler.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/ConnectionHandler.java deleted file mode 100644 index 89c0daf..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/ConnectionHandler.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.server; - -import io.reactivex.netty.channel.Connection; -import rx.Observable; - -/** - * A connection handler invoked for every new connection is established by {@link TcpServer} - * - * @param The type of the object that is read from a new connection. - * @param The type of objects that are written to a new connection. - */ -public interface ConnectionHandler { - - /** - * Invoked whenever a new connection is established. - * - * @param newConnection Newly established connection. - * - * @return An {@link Observable}, unsubscribe from which should cancel the handling. - */ - Observable handle(Connection newConnection); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServer.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServer.java deleted file mode 100644 index e59cd10..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServer.java +++ /dev/null @@ -1,442 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.server; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.ServerChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.ssl.SslHandler; -import io.netty.util.concurrent.EventExecutorGroup; -import io.reactivex.netty.events.EventSource; -import io.reactivex.netty.protocol.tcp.server.events.TcpServerEventListener; -import io.reactivex.netty.protocol.tcp.server.events.TcpServerEventPublisher; -import io.reactivex.netty.ssl.SslCodec; -import rx.functions.Action1; -import rx.functions.Func0; -import rx.functions.Func1; - -import javax.net.ssl.SSLEngine; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.concurrent.TimeUnit; - -/** - * A TCP server. - * - *

Immutability

- * An instance of this server is immutable and all mutations produce a new server instance. - * - * @param The type of objects read from this server. - * @param The type of objects written to this server. - */ -public abstract class TcpServer implements EventSource { - - /** - * Creates a new server instance, inheriting all configurations from this server and adding a - * {@link ChannelOption} for the server socket created by the newly created server instance. - * - * @param option Option to add. - * @param value Value for the option. - * - * @return A new {@link TcpServer} instance. - */ - public abstract TcpServer channelOption(ChannelOption option, T value); - - /** - * Creates a new server instance, inheriting all configurations from this server and adding a - * {@link ChannelOption} for the client socket created by the newly created server instance. - * - * @param option Option to add. - * @param value Value for the option. - * - * @return A new {@link TcpServer} instance. - */ - public abstract TcpServer clientChannelOption(ChannelOption option, T value); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this server. - * The specified handler is added at the first position of the pipeline as specified by - * {@link ChannelPipeline#addFirst(String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} - * will be more convenient. - * - * @param name Name of the handler. - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link TcpServer} instance. - */ - public abstract TcpServer addChannelHandlerFirst(String name, Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this server. The specified - * handler is added at the first position of the pipeline as specified by - * {@link ChannelPipeline#addFirst(EventExecutorGroup, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param group The {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} - * methods - * @param name The name of the handler to append - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link TcpServer} instance. - */ - public abstract TcpServer addChannelHandlerFirst(EventExecutorGroup group, String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this server. The specified - * handler is added at the last position of the pipeline as specified by - * {@link ChannelPipeline#addLast(String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be - * more convenient. - * - * @param name Name of the handler. - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link TcpServer} instance. - */ - public abstract TcpServer addChannelHandlerLast(String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this server. The specified - * handler is added at the last position of the pipeline as specified by - * {@link ChannelPipeline#addLast(EventExecutorGroup, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} - * methods - * @param name the name of the handler to append - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link TcpServer} instance. - */ - public abstract TcpServer addChannelHandlerLast(EventExecutorGroup group, String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this server. The specified - * handler is added before an existing handler with the passed {@code baseName} in the pipeline as specified by - * {@link ChannelPipeline#addBefore(String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param baseName the name of the existing handler - * @param name Name of the handler. - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link TcpServer} instance. - */ - public abstract TcpServer addChannelHandlerBefore(String baseName, String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this server. The specified - * handler is added before an existing handler with the passed {@code baseName} in the pipeline as specified by - * {@link ChannelPipeline#addBefore(EventExecutorGroup, String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} - * methods - * @param baseName the name of the existing handler - * @param name the name of the handler to append - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link TcpServer} instance. - */ - public abstract TcpServer addChannelHandlerBefore(EventExecutorGroup group, String baseName, - String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this server. The specified - * handler is added after an existing handler with the passed {@code baseName} in the pipeline as specified by - * {@link ChannelPipeline#addAfter(String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param baseName the name of the existing handler - * @param name Name of the handler. - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link TcpServer} instance. - */ - public abstract TcpServer addChannelHandlerAfter(String baseName, String name, - Func0 handlerFactory); - - /** - * Adds a {@link ChannelHandler} to {@link ChannelPipeline} for all connections created by this server. The specified - * handler is added after an existing handler with the passed {@code baseName} in the pipeline as specified by - * {@link ChannelPipeline#addAfter(EventExecutorGroup, String, String, ChannelHandler)} - * - * For better flexibility of pipeline modification, the method {@link #pipelineConfigurator(Action1)} will be more - * convenient. - * - * @param group the {@link EventExecutorGroup} which will be used to execute the {@link ChannelHandler} - * methods - * @param baseName the name of the existing handler - * @param name the name of the handler to append - * @param handlerFactory Factory to create handler instance to add. - * - * @return A new {@link TcpServer} instance. - */ - public abstract TcpServer addChannelHandlerAfter(EventExecutorGroup group, String baseName, - String name, Func0 handlerFactory); - - /** - * Creates a new server instances, inheriting all configurations from this server and using the passed - * action to configure all the connections created by the newly created server instance. - * - * @param pipelineConfigurator Action to configure {@link ChannelPipeline}. - * - * @return A new {@link TcpServer} instance. - */ - public abstract TcpServer pipelineConfigurator(Action1 pipelineConfigurator); - - /** - * Creates a new server instances, inheriting all configurations from this server and using the passed - * {@code sslEngineFactory} for all secured connections accepted by the newly created server instance. - * - * If the {@link SSLEngine} instance can be statically, created, {@link #secure(SSLEngine)} can be used. - * - * @param sslEngineFactory Factory for all secured connections created by the newly created server instance. - * - * @return A new {@link TcpServer} instance. - */ - public abstract TcpServer secure(Func1 sslEngineFactory); - - /** - * Creates a new server instances, inheriting all configurations from this server and using the passed - * {@code sslEngine} for all secured connections accepted by the newly created server instance. - * - * If the {@link SSLEngine} instance can not be statically, created, {@link #secure(Func1)} )} can be used. - * - * @param sslEngine {@link SSLEngine} for all secured connections created by the newly created server instance. - * - * @return A new {@link TcpServer} instance. - */ - public abstract TcpServer secure(SSLEngine sslEngine); - - /** - * Creates a new server instances, inheriting all configurations from this server and using the passed - * {@code sslCodec} for all secured connections accepted by the newly created server instance. - * - * This is required only when the {@link SslHandler} used by {@link SslCodec} is to be modified before adding to - * the {@link ChannelPipeline}. For most of the cases, {@link #secure(Func1)} or {@link #secure(SSLEngine)} will be - * enough. - * - * @param sslCodec {@link SslCodec} for all secured connections created by the newly created server instance. - * - * @return A new {@link TcpServer} instance. - */ - public abstract TcpServer secure(SslCodec sslCodec); - - /** - * Creates a new server instances, inheriting all configurations from this server and using a self-signed - * certificate for all secured connections accepted by the newly created server instance. - * - * This is only for testing and should not be used for real production servers. - * - * @return A new {@link TcpServer} instance. - */ - public abstract TcpServer unsafeSecure(); - - /** - * Creates a new server instances, inheriting all configurations from this server and enabling wire logging at the - * passed level for the newly created server instance. - * - * @param wireLoggingLevel Logging level at which the wire logs will be logged. The wire logging will only be done if - * logging is enabled at this level for {@link io.netty.handler.logging.LoggingHandler} - * - * @return A new {@link TcpServer} instance. - * - * @deprecated Use {@link #enableWireLogging(String, LogLevel)} instead. - */ - @Deprecated - public abstract TcpServer enableWireLogging(LogLevel wireLoggingLevel); - - /** - * Creates a new server instances, inheriting all configurations from this server and enabling wire logging at the - * passed level for the newly created server instance. - * - * @param name Name of the logger that can be used to control the logging dynamically. - * @param wireLoggingLevel Logging level at which the wire logs will be logged. The wire logging will only be done if - * logging is enabled at this level for {@link io.netty.handler.logging.LoggingHandler} - * - * @return A new {@link TcpServer} instance. - */ - public abstract TcpServer enableWireLogging(String name, LogLevel wireLoggingLevel); - - /** - * Returns the port at which this server is running. - * - * For servers using ephemeral ports, this would return the actual port used, only after the server is started. - * - * @return The port at which this server is running. - */ - public abstract int getServerPort(); - - /** - * Returns the address at which this server is running. - * - * @return The address at which this server is running. - */ - public abstract SocketAddress getServerAddress(); - - /** - * Starts this server. - * - * @param connectionHandler Connection handler that will handle any new server connections to this server. - * - * @return This server. - */ - public abstract TcpServer start(ConnectionHandler connectionHandler); - - /** - * Shutdown this server and waits till the server socket is closed. - */ - public abstract void shutdown(); - - /** - * Waits for the shutdown of this server. - * - * This does not actually shutdown the server. It just waits for some other action to shutdown. - */ - public abstract void awaitShutdown(); - - /** - * Waits for the shutdown of this server, waiting a maximum of the passed duration. - * - * This does not actually shutdown the server. It just waits for some other action to shutdown. - * - * @param duration Duration to wait for shutdown. - * @param timeUnit Timeunit for the duration to wait for shutdown. - */ - public abstract void awaitShutdown(long duration, TimeUnit timeUnit); - - /** - * Returns the event publisher for this server. - * - * @return The event publisher for this server. - */ - public abstract TcpServerEventPublisher getEventPublisher(); - - /** - * Creates a new server using an ephemeral port. The port used can be queried after starting this server, using - * {@link #getServerPort()} - * - * @return A new {@link TcpServer} - */ - public static TcpServer newServer() { - return newServer(0); - } - - /** - * Creates a new server using the passed port. - * - * @param port Port for the server. {@code 0} to use ephemeral port. - * @return A new {@link TcpServer} - */ - public static TcpServer newServer(int port) { - return new TcpServerImpl<>(new InetSocketAddress(port)); - } - - /** - * Creates a new server using the passed port. - * - * @param port Port for the server. {@code 0} to use ephemeral port. - * @param eventLoopGroup Eventloop group to be used for server as well as client sockets. - * @param channelClass The class to be used for server channel. - * - * @return A new {@link TcpServer} - */ - public static TcpServer newServer(int port, EventLoopGroup eventLoopGroup, - Class channelClass) { - return newServer(port, eventLoopGroup, eventLoopGroup, channelClass); - } - - /** - * Creates a new server using the passed port. - * - * @param port Port for the server. {@code 0} to use ephemeral port. - * @param acceptGroup Eventloop group to be used for server sockets. - * @param clientGroup Eventloop group to be used for client sockets. - * @param channelClass The class to be used for server channel. - * - * @return A new {@link TcpServer} - */ - public static TcpServer newServer(int port, EventLoopGroup acceptGroup, - EventLoopGroup clientGroup, - Class channelClass) { - return newServer(new InetSocketAddress(port), acceptGroup, clientGroup, channelClass); - } - - /** - * Creates a new server using the passed address. - * - * @param socketAddress Socket address for the server. - * @return A new {@link TcpServer} - */ - public static TcpServer newServer(SocketAddress socketAddress) { - return new TcpServerImpl<>(socketAddress); - } - - /** - * Creates a new server using the passed address. - * - * @param socketAddress Socket address for the server. - * @param eventLoopGroup Eventloop group to be used for server as well as client sockets. - * @param channelClass The class to be used for server channel. - * - * @return A new {@link TcpServer} - */ - public static TcpServer newServer(SocketAddress socketAddress, EventLoopGroup eventLoopGroup, - Class channelClass) { - return new TcpServerImpl<>(socketAddress, eventLoopGroup, eventLoopGroup, channelClass); - } - - /** - * Creates a new server using the passed address. - * - * @param socketAddress Socket address for the server. - * @param acceptGroup Eventloop group to be used for server sockets. - * @param clientGroup Eventloop group to be used for client sockets. - * @param channelClass The class to be used for server channel. - * - * @return A new {@link TcpServer} - */ - public static TcpServer newServer(SocketAddress socketAddress, EventLoopGroup acceptGroup, - EventLoopGroup clientGroup, - Class channelClass) { - return new TcpServerImpl<>(socketAddress, acceptGroup, clientGroup, channelClass); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServerConnectionToChannelBridge.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServerConnectionToChannelBridge.java deleted file mode 100644 index 88a7eed..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServerConnectionToChannelBridge.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.server; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.reactivex.netty.channel.AbstractConnectionToChannelBridge; -import io.reactivex.netty.channel.ChannelSubscriberEvent; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.ConnectionImpl; -import io.reactivex.netty.channel.EmitConnectionEvent; -import io.reactivex.netty.events.Clock; -import io.reactivex.netty.events.EventAttributeKeys; -import io.reactivex.netty.protocol.tcp.server.events.TcpServerEventPublisher; -import rx.Observable; -import rx.Subscriber; -import rx.functions.Action0; -import rx.functions.Func1; - -import java.nio.channels.ClosedChannelException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static java.util.concurrent.TimeUnit.*; - -/** - * An implementation of {@link AbstractConnectionToChannelBridge} for servers. - * - * @param The type of objects read from the server using this bridge. - * @param The type of objects written to this server using this bridge. - */ -public class TcpServerConnectionToChannelBridge extends AbstractConnectionToChannelBridge { - - private static final Logger logger = Logger.getLogger(TcpServerConnectionToChannelBridge.class.getName()); - private static final String HANDLER_NAME = "server-conn-channel-bridge"; - - private final ConnectionHandler connectionHandler; - private final TcpServerEventPublisher eventPublisher; - private final boolean isSecure; - private final ChannelSubscriberEvent channelSubscriberEvent; - - private TcpServerConnectionToChannelBridge(ConnectionHandler connectionHandler, - TcpServerEventPublisher eventPublisher, boolean isSecure) { - super(HANDLER_NAME, eventPublisher, eventPublisher); - this.connectionHandler = connectionHandler; - this.eventPublisher = eventPublisher; - this.isSecure = isSecure; - channelSubscriberEvent = new ChannelSubscriberEvent<>(new NewChannelSubscriber()); - } - - @Override - public void channelRegistered(ChannelHandlerContext ctx) throws Exception { - userEventTriggered(ctx, channelSubscriberEvent); - if (!isSecure) {/*When secure, the event is triggered post SSL handshake via the SslCodec*/ - userEventTriggered(ctx, EmitConnectionEvent.INSTANCE); - } - super.channelRegistered(ctx); - } - - public static TcpServerConnectionToChannelBridge addToPipeline(ChannelPipeline pipeline, - ConnectionHandler connectionHandler, - TcpServerEventPublisher eventPublisher, - boolean isSecure) { - TcpServerConnectionToChannelBridge toAdd = new TcpServerConnectionToChannelBridge<>(connectionHandler, - eventPublisher, isSecure); - pipeline.addLast(HANDLER_NAME, toAdd); - return toAdd; - } - - private final class NewChannelSubscriber extends Subscriber { - - @Override - public void onCompleted() { - // No Op. - } - - @Override - public void onError(Throwable e) { - logger.log(Level.SEVERE, "Error while listening for new client connections.", e); - } - - @Override - public void onNext(final Channel channel) { - channel.attr(EventAttributeKeys.EVENT_PUBLISHER).set(eventPublisher); - channel.attr(EventAttributeKeys.CONNECTION_EVENT_LISTENER).set(eventPublisher); - - final Connection connection = ConnectionImpl.fromChannel(channel); - final long startTimeNanos = eventPublisher.publishingEnabled() ? Clock.newStartTimeNanos() : -1; - if (eventPublisher.publishingEnabled()) { - eventPublisher.onNewClientConnected(); - } - Observable handledObservable; - try { - if (eventPublisher.publishingEnabled()) { - eventPublisher.onConnectionHandlingStart(Clock.onEndNanos(startTimeNanos), NANOSECONDS); - } - handledObservable = connectionHandler.handle(connection); - } catch (Throwable throwable) { - handledObservable = Observable.error(throwable); - } - - if (null == handledObservable) { - logger.log(Level.SEVERE, "Connection handler returned null."); - handledObservable = Observable.empty(); - } - - handledObservable - .onErrorResumeNext( - new Func1>() { - @Override - public Observable call(Throwable throwable) { - if (throwable instanceof ClosedChannelException) { - return Observable.empty(); - } else { - /*Since, this is always reading input for new requests, it will always get a - closed channel exception on connection close from client. No point in logging - that error.*/ - if (eventPublisher.publishingEnabled()) { - eventPublisher.onConnectionHandlingFailed(Clock.onEndNanos(startTimeNanos), - NANOSECONDS, - throwable); - } - logger.log(Level.SEVERE, "Error processing connection.", throwable); - return connection.close(); - } - } - }) - .ambWith(connection.closeListener()) - .concatWith(connection.close()) - .doOnCompleted(new Action0() { - @Override - public void call() { - if (eventPublisher.publishingEnabled()) { - eventPublisher.onConnectionHandlingSuccess(Clock.onEndNanos(startTimeNanos), - NANOSECONDS); - } - } - }) - .subscribe(); - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServerImpl.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServerImpl.java deleted file mode 100644 index 362b952..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServerImpl.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.server; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.ServerChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.util.concurrent.EventExecutorGroup; -import io.reactivex.netty.protocol.tcp.server.events.TcpServerEventListener; -import io.reactivex.netty.protocol.tcp.server.events.TcpServerEventPublisher; -import io.reactivex.netty.server.ServerState; -import io.reactivex.netty.ssl.SslCodec; -import rx.Subscription; -import rx.functions.Action1; -import rx.functions.Func0; -import rx.functions.Func1; - -import javax.net.ssl.SSLEngine; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class TcpServerImpl extends TcpServer { - - private static final Logger logger = Logger.getLogger(TcpServerImpl.class.getName()); - - protected enum ServerStatus {Created, Starting, Started, Shutdown} - - private final ServerState state; - private ChannelFuture bindFuture; - protected final AtomicReference serverStateRef; - - public TcpServerImpl(SocketAddress socketAddress) { - state = TcpServerState.create(socketAddress); - serverStateRef = new AtomicReference<>(ServerStatus.Created); - } - - public TcpServerImpl(SocketAddress socketAddress, EventLoopGroup parent, EventLoopGroup child, - Class channelClass) { - state = TcpServerState.create(socketAddress, parent, child, channelClass); - serverStateRef = new AtomicReference<>(ServerStatus.Created); - } - - private TcpServerImpl(ServerState state) { - this.state = state; - serverStateRef = new AtomicReference<>(ServerStatus.Created); - } - - @Override - public TcpServer channelOption(ChannelOption option, T value) { - return copy(state.channelOption(option, value)); - } - - @Override - public TcpServer clientChannelOption(ChannelOption option, T value) { - return copy(state.clientChannelOption(option, value)); - } - - @Override - public TcpServer addChannelHandlerFirst(String name, Func0 handlerFactory) { - return copy(state.addChannelHandlerFirst(name, handlerFactory)); - } - - @Override - public TcpServer addChannelHandlerFirst(EventExecutorGroup group, String name, - Func0 handlerFactory) { - return copy(state.addChannelHandlerFirst(group, name, handlerFactory)); - } - - @Override - public TcpServer addChannelHandlerLast(String name, Func0 handlerFactory) { - return copy(state.addChannelHandlerLast(name, handlerFactory)); - } - - @Override - public TcpServer addChannelHandlerLast(EventExecutorGroup group, String name, - Func0 handlerFactory) { - return copy(state.addChannelHandlerLast(group, name, handlerFactory)); - } - - @Override - public TcpServer addChannelHandlerBefore(String baseName, String name, Func0 handlerFactory) { - return copy(state.addChannelHandlerBefore(baseName, name, handlerFactory)); - } - - @Override - public TcpServer addChannelHandlerBefore(EventExecutorGroup group, String baseName, String name, - Func0 handlerFactory) { - return copy(state.addChannelHandlerBefore(group, baseName, name, handlerFactory)); - } - - @Override - public TcpServer addChannelHandlerAfter(String baseName, String name, Func0 handlerFactory) { - return copy(state.addChannelHandlerAfter(baseName, name, handlerFactory)); - } - - @Override - public TcpServer addChannelHandlerAfter(EventExecutorGroup group, String baseName, String name, - Func0 handlerFactory) { - return copy(state.addChannelHandlerAfter(group, baseName, name, handlerFactory)); - } - - @Override - public TcpServer pipelineConfigurator(Action1 pipelineConfigurator) { - return copy(state.pipelineConfigurator(pipelineConfigurator)); - } - - @Override - public TcpServer secure(Func1 sslEngineFactory) { - return copy(((TcpServerState)state).secure(sslEngineFactory)); - } - - @Override - public TcpServer secure(SSLEngine sslEngine) { - return copy(((TcpServerState)state).secure(sslEngine)); - } - - @Override - public TcpServer secure(SslCodec sslCodec) { - return copy(((TcpServerState)state).secure(sslCodec)); - } - - @Override - public TcpServer unsafeSecure() { - return copy(((TcpServerState)state).unsafeSecure()); - } - - @Override - @Deprecated - public TcpServer enableWireLogging(LogLevel wireLoggingLevel) { - return copy(state.enableWireLogging(wireLoggingLevel)); - } - - @Override - public TcpServer enableWireLogging(String name, LogLevel wireLoggingLevel) { - return copy(state.enableWireLogging(name, wireLoggingLevel)); - } - - @Override - public int getServerPort() { - - final SocketAddress localAddress = getServerAddress(); - if (localAddress instanceof InetSocketAddress) { - return ((InetSocketAddress) localAddress).getPort(); - } else { - return 0; - } - } - - @Override - public SocketAddress getServerAddress() { - SocketAddress localAddress; - if (null != bindFuture && bindFuture.isDone()) { - localAddress = bindFuture.channel().localAddress(); - } else { - localAddress = state.getServerAddress(); - } - return localAddress; - } - - @Override - public TcpServer start(final ConnectionHandler connectionHandler) { - if (!serverStateRef.compareAndSet(ServerStatus.Created, ServerStatus.Starting)) { - throw new IllegalStateException("Server already started"); - } - try { - Action1 handlerFactory = new Action1() { - @Override - public void call(ChannelPipeline pipeline) { - TcpServerState tcpState = (TcpServerState) state; - TcpServerConnectionToChannelBridge.addToPipeline(pipeline, connectionHandler, - tcpState.getEventPublisher(), tcpState.isSecure()); - } - }; - final TcpServerState newState = (TcpServerState) state.pipelineConfigurator(handlerFactory); - bindFuture = newState.getBootstrap().bind(newState.getServerAddress()).sync(); - if (!bindFuture.isSuccess()) { - throw new RuntimeException(bindFuture.cause()); - } - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - serverStateRef.set(ServerStatus.Started); // It will come here only if this was the thread that transitioned to Starting - - logger.info("Rx server started at port: " + getServerPort()); - - return this; - } - - @Override - public void shutdown() { - if (!serverStateRef.compareAndSet(ServerStatus.Started, ServerStatus.Shutdown)) { - throw new IllegalStateException("The server is already shutdown."); - } else { - try { - bindFuture.channel().close().sync(); - } catch (InterruptedException e) { - logger.log(Level.SEVERE, "Interrupted while waiting for the server socket to close.", e); - } - } - } - - @Override - public void awaitShutdown() { - ServerStatus status = serverStateRef.get(); - switch (status) { - case Created: - case Starting: - throw new IllegalStateException("Server not started yet."); - case Started: - try { - bindFuture.channel().closeFuture().await(); - } catch (InterruptedException e) { - Thread.interrupted(); // Reset the interrupted status - logger.log(Level.SEVERE, "Interrupted while waiting for the server socket to close.", e); - } - break; - case Shutdown: - // Nothing to do as it is already shutdown. - break; - } - } - - @Override - public void awaitShutdown(long duration, TimeUnit timeUnit) { - ServerStatus status = serverStateRef.get(); - switch (status) { - case Created: - case Starting: - throw new IllegalStateException("Server not started yet."); - case Started: - try { - bindFuture.channel().closeFuture().await(duration, timeUnit); - } catch (InterruptedException e) { - Thread.interrupted(); // Reset the interrupted status - logger.log(Level.SEVERE, "Interrupted while waiting for the server socket to close.", e); - } - break; - case Shutdown: - // Nothing to do as it is already shutdown. - break; - } - } - - @Override - public TcpServerEventPublisher getEventPublisher() { - return ((TcpServerState)state).getEventPublisher(); - } - - @Override - public Subscription subscribe(TcpServerEventListener listener) { - return ((TcpServerState)state).getEventPublisher().subscribe(listener); - } - - private static TcpServer copy(ServerState newState) { - return new TcpServerImpl<>(newState); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServerInterceptorChain.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServerInterceptorChain.java deleted file mode 100644 index e89157f..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServerInterceptorChain.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.server; - -import io.netty.buffer.ByteBuf; -import io.reactivex.netty.channel.Connection; -import rx.annotations.Beta; - -/** - * A utility to create an interceptor chain to be used with a {@link TcpServer} to modify behavior of connections - * accepted by that server. - * - *

What are interceptors?

- * - * Interceptors can be used to achieve use-cases that involve instrumentation related to any behavior of the connection, - * they can even be used to short-circuit the rest of the chain or to provide canned responses. In order to achieve such - * widely different use-cases, an interceptor is modelled as a simple function that takes one {@link ConnectionHandler} - * and returns another {@link ConnectionHandler} instance. With this low level abstraction, any use-case pertaining to - * connection instrumentation can be achieved. - * - * - * An interceptor chain always starts with an interceptor and ends with a {@link ConnectionHandler} and any number of - * other interceptors can exist between the start and end.

- * - * A chain can be created by using the various {@code start*()} methods available in this class, eg: - * {@link #start(Interceptor)}, {@link #startRaw(Interceptor)}.

- * - * After starting a chain, any number of other interceptors can be added by using the various {@code next*()} methods - * available in this class, eg: {@link #next(Interceptor)}, {@link #nextWithTransform(TransformingInterceptor)}, - * {@link #nextWithReadTransform(TransformingInterceptor)} and {@link #nextWithWriteTransform(TransformingInterceptor)}

- * - * After adding the required interceptors, by providing a {@link ConnectionHandler} via the - * {@link #end(ConnectionHandler)} method, the chain can be ended and the returned {@link ConnectionHandler} can be used - * with any {@link TcpServer}

- * - * So, a typical interaction with this class would look like:

- * - * {@code - * TcpServer.newServer().start(TcpServerInterceptorChain.start(first).next(second).next(third).end(handler)) - * } - * - *

Simple Interceptor

- * - * For interceptors that do not change the types of objects read or written to the underlying connection, the interface - * {@link Interceptor} defines the interceptor contract. - * - *

Modifying the type of data read/written to the {@link Connection}

- * - * Sometimes, it is required to change the type of objects read or written to a {@link Connection} instance handled by - * a {@link TcpServer}. For such cases, the interface {@link TransformingInterceptor} defines - * the interceptor contract. Since, this included 4 generic arguments to the interceptor, this is not the base type for - * all interceptors and should be used only when the types of the {@link Connection} are actually to be changed. - * - * - * The above diagram depicts the execution order of interceptors. The first connection handler (internal) is created by - * this class and as is returned by {@link #end(ConnectionHandler)} method by providing a {@link ConnectionHandler} that - * does the actual processing of the connection. {@link TcpServer} with which this interceptor chain is used, will - * invoke the internal connection handler provided by this class.

- * - * The interceptors are invoked in the order that they are added to this chain. - * - * @param The type of objects read from a connection to {@link TcpServer} with which this interceptor chain will be - * used. - * @param The type of objects written to a connection to {@link TcpServer} with which this interceptor chain will be - * used. - * @param The type of objects read from a connection to {@link TcpServer} after applying this interceptor chain. - * @param The type of objects written to a connection to {@link TcpServer} after applying this interceptor chain. - */ -@Beta -public final class TcpServerInterceptorChain { - - private final TransformingInterceptor interceptor; - - private TcpServerInterceptorChain(TransformingInterceptor interceptor) { - this.interceptor = interceptor; - } - - /** - * Add the next interceptor to this chain. - * - * @param next Next interceptor to add. - * - * @return A new interceptor chain with the interceptors current existing and the passed interceptor added to the - * end. - */ - public TcpServerInterceptorChain next(final Interceptor next) { - return new TcpServerInterceptorChain<>(new TransformingInterceptor() { - @Override - public ConnectionHandler intercept(ConnectionHandler handler) { - return interceptor.intercept(next.intercept(handler)); - } - }); - } - - /** - * Add the next interceptor to this chain, which changes the type of objects read from the connections processed by - * the associated {@link TcpServer}. - * - * @param next Next interceptor to add. - * - * @return A new interceptor chain with the interceptors current existing and the passed interceptor added to the - * end. - */ - public TcpServerInterceptorChain nextWithReadTransform(final TransformingInterceptor next) { - return new TcpServerInterceptorChain<>(new TransformingInterceptor() { - @Override - public ConnectionHandler intercept(ConnectionHandler handler) { - return interceptor.intercept(next.intercept(handler)); - } - }); - } - - /** - * Add the next interceptor to this chain, which changes the type of objects written to the connections processed by - * the associated {@link TcpServer}. - * - * @param next Next interceptor to add. - * - * @return A new interceptor chain with the interceptors current existing and the passed interceptor added to the - * end. - */ - public TcpServerInterceptorChain nextWithWriteTransform(final TransformingInterceptor next) { - return new TcpServerInterceptorChain<>(new TransformingInterceptor() { - @Override - public ConnectionHandler intercept(ConnectionHandler handler) { - return interceptor.intercept(next.intercept(handler)); - } - }); - } - - /** - * Add the next interceptor to this chain, which changes the type of objects read and written from/to the - * connections processed by the associated {@link TcpServer}. - * - * @param next Next interceptor to add. - * - * @return A new interceptor chain with the interceptors current existing and the passed interceptor added to the - * end. - */ - public TcpServerInterceptorChain nextWithTransform(final TransformingInterceptor next) { - return new TcpServerInterceptorChain<>(new TransformingInterceptor() { - @Override - public ConnectionHandler intercept(ConnectionHandler handler) { - return interceptor.intercept(next.intercept(handler)); - } - }); - } - - /** - * Terminates this chain with the passed {@link ConnectionHandler} and returns a {@link ConnectionHandler} to be - * used by a {@link TcpServer} - * - * @param handler Connection handler to use. - * - * @return A connection handler that wires the interceptor chain, to be used with {@link TcpServer} instead of - * directly using the passed {@code handler} - */ - public ConnectionHandler end(ConnectionHandler handler) { - return interceptor.intercept(handler); - } - - /** - * One of the methods to start creating the interceptor chain. The other start methods can be used for starting with - * interceptors that modify the type of Objects read/written from/to the connections processed by the associated - * {@link TcpServer}. - * - * @param start The starting interceptor for this chain. - * - * @param The type of objects read from a connection to {@link TcpServer} with which this interceptor chain will - * be used. - * @param The type of objects written to a connection to {@link TcpServer} with which this interceptor chain - * will be used. - * - * @return A new interceptor chain. - */ - public static TcpServerInterceptorChain start(final Interceptor start) { - return new TcpServerInterceptorChain<>(new TransformingInterceptor() { - @Override - public ConnectionHandler intercept(ConnectionHandler handler) { - return start.intercept(handler); - } - }); - } - - /** - * One of the methods to start creating the interceptor chain. The other start methods can be used for starting with - * interceptors that modify the type of Objects read/written from/to the connections processed by the associated - * {@link TcpServer}. - * - * @param start The starting interceptor for this chain. - * - * @return A new interceptor chain. - */ - public static TcpServerInterceptorChain startRaw(final Interceptor start) { - return new TcpServerInterceptorChain<>(new TransformingInterceptor() { - @Override - public ConnectionHandler intercept(ConnectionHandler handler) { - return start.intercept(handler); - } - }); - } - - /** - * An interceptor that preserves the type of objects read and written to the connection. - * - * @param Type of objects read from the connection handled by this interceptor. - * @param Type of objects written to the connection handled by this interceptor. - */ - public interface Interceptor { - - /** - * Intercepts and optionally changes the passed {@code ConnectionHandler}. - * - * @param handler Handler to intercept. - * - * @return Handler to use after this transformation. - */ - ConnectionHandler intercept(ConnectionHandler handler); - - } - - /** - * An interceptor that changes the type of objects read and written to the connection. - * - * @param Type of objects read from the connection before applying this interceptor. - * @param Type of objects written to the connection before applying this interceptor. - * @param Type of objects read from the connection after applying this interceptor. - * @param Type of objects written to the connection after applying this interceptor. - */ - public interface TransformingInterceptor { - - /** - * Intercepts and changes the passed {@code ConnectionHandler}. - * - * @param handler Handler to intercept. - * - * @return Handler to use after this transformation. - */ - ConnectionHandler intercept(ConnectionHandler handler); - - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServerState.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServerState.java deleted file mode 100644 index 64f00a1..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/TcpServerState.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.server; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.ServerChannel; -import io.netty.channel.epoll.EpollServerSocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; -import io.reactivex.netty.RxNetty; -import io.reactivex.netty.channel.DetachedChannelPipeline; -import io.reactivex.netty.protocol.tcp.server.events.TcpServerEventPublisher; -import io.reactivex.netty.server.ServerState; -import io.reactivex.netty.ssl.DefaultSslCodec; -import io.reactivex.netty.ssl.SslCodec; -import rx.exceptions.Exceptions; -import rx.functions.Func1; - -import javax.net.ssl.SSLEngine; -import java.net.SocketAddress; - -/** - * A collection of state that {@link TcpServer} holds. - * This supports the copy-on-write semantics of {@link TcpServer} - * - * @param The type of objects read from the server owning this state. - * @param The type of objects written to the server owning this state. - */ -public class TcpServerState extends ServerState { - - private final TcpServerEventPublisher eventPublisher; - private final boolean secure; - - protected TcpServerState(SocketAddress socketAddress, EventLoopGroup parent, - EventLoopGroup child, - Class channelClass) { - super(socketAddress, parent, child, channelClass); - secure = false; - eventPublisher = new TcpServerEventPublisher(); - } - - protected TcpServerState(TcpServerState toCopy, SslCodec sslCodec) { - super(toCopy, toCopy.detachedPipeline.configure(sslCodec)); - secure = true; - eventPublisher = toCopy.eventPublisher.copy(); - } - - protected TcpServerState(TcpServerState toCopy, SocketAddress socketAddress) { - super(toCopy, socketAddress); - secure = toCopy.secure; - eventPublisher = toCopy.eventPublisher.copy(); - } - - protected TcpServerState(TcpServerState toCopy, ServerBootstrap clone) { - super(toCopy, clone); - secure = toCopy.secure; - eventPublisher = toCopy.eventPublisher.copy(); - } - - protected TcpServerState(TcpServerState toCopy, DetachedChannelPipeline newPipeline) { - super(toCopy, newPipeline); - secure = toCopy.secure; - eventPublisher = toCopy.eventPublisher.copy(); - } - - public TcpServerState secure(Func1 sslEngineFactory) { - return secure(new DefaultSslCodec(sslEngineFactory)); - } - - public TcpServerState secure(SSLEngine sslEngine) { - return secure(new DefaultSslCodec(sslEngine)); - } - - public TcpServerState secure(SslCodec sslCodec) { - return new TcpServerState<>(this, sslCodec); - } - - public TcpServerState unsafeSecure() { - return secure(new DefaultSslCodec(new Func1() { - @Override - public SSLEngine call(ByteBufAllocator allocator) { - SelfSignedCertificate ssc; - try { - ssc = new SelfSignedCertificate(); - return SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .build() - .newEngine(allocator); - } catch (Exception e) { - throw Exceptions.propagate(e); - } - } - })); - } - - @Override - protected ServerState copyBootstrapOnly() { - return new TcpServerState<>(this, bootstrap.clone()); - } - - @Override - protected ServerState copy() { - return new TcpServerState<>(this, detachedPipeline.copy()); - } - - @Override - protected ServerState copy(SocketAddress newSocketAddress) { - return new TcpServerState<>(this, socketAddress); - } - - public boolean isSecure() { - return secure; - } - - public TcpServerEventPublisher getEventPublisher() { - return eventPublisher; - } - - /*package private. Should not leak as it is mutable*/ ServerBootstrap getBootstrap() { - return bootstrap; - } - - public static TcpServerState create(SocketAddress socketAddress) { - return create(socketAddress, RxNetty.getRxEventLoopProvider().globalServerEventLoop(true), - RxNetty.isUsingNativeTransport() ? EpollServerSocketChannel.class : NioServerSocketChannel.class); - } - - public static TcpServerState create(SocketAddress socketAddress, EventLoopGroup group, - Class channelClass) { - return create(socketAddress, group, group, channelClass); - } - - public static TcpServerState create(SocketAddress socketAddress, EventLoopGroup parent, - EventLoopGroup child, - Class channelClass) { - return new TcpServerState<>(socketAddress, parent, child, channelClass); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/events/SafeTcpServerEventListener.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/events/SafeTcpServerEventListener.java deleted file mode 100644 index 7315981..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/events/SafeTcpServerEventListener.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.server.events; - -import io.reactivex.netty.events.internal.SafeEventListener; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -final class SafeTcpServerEventListener extends TcpServerEventListener implements SafeEventListener { - - private final TcpServerEventListener delegate; - private final AtomicBoolean completed = new AtomicBoolean(); - - public SafeTcpServerEventListener(TcpServerEventListener delegate) { - this.delegate = delegate; - } - - @Override - public void onCompleted() { - if (completed.compareAndSet(false, true)) { - delegate.onCompleted(); - } - } - - @Override - public void onNewClientConnected() { - if (!completed.get()) { - delegate.onNewClientConnected(); - } - } - - @Override - public void onConnectionHandlingStart(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onConnectionHandlingStart(duration, timeUnit); - } - } - - @Override - public void onConnectionHandlingSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onConnectionHandlingSuccess(duration, timeUnit); - } - } - - @Override - public void onConnectionHandlingFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - if (!completed.get()) { - delegate.onConnectionHandlingFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onByteRead(long bytesRead) { - if (!completed.get()) { - delegate.onByteRead(bytesRead); - } - } - - @Override - public void onByteWritten(long bytesWritten) { - if (!completed.get()) { - delegate.onByteWritten(bytesWritten); - } - } - - @Override - public void onFlushStart() { - if (!completed.get()) { - delegate.onFlushStart(); - } - } - - @Override - public void onFlushComplete(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onFlushComplete(duration, timeUnit); - } - } - - @Override - public void onWriteStart() { - if (!completed.get()) { - delegate.onWriteStart(); - } - } - - @Override - public void onWriteSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onWriteSuccess(duration, timeUnit); - } - } - - @Override - public void onWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - if (!completed.get()) { - delegate.onWriteFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onConnectionCloseStart() { - if (!completed.get()) { - delegate.onConnectionCloseStart(); - } - } - - @Override - public void onConnectionCloseSuccess(long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onConnectionCloseSuccess(duration, timeUnit); - } - } - - @Override - public void onConnectionCloseFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - if (!completed.get()) { - delegate.onConnectionCloseFailed(duration, timeUnit, throwable); - } - } - - @Override - public void onCustomEvent(Object event) { - if (!completed.get()) { - delegate.onCustomEvent(event); - } - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { - if (!completed.get()) { - delegate.onCustomEvent(event, duration, timeUnit); - } - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { - if (!completed.get()) { - delegate.onCustomEvent(event, duration, timeUnit, throwable); - } - } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { - if (!completed.get()) { - delegate.onCustomEvent(event, throwable); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof SafeTcpServerEventListener)) { - return false; - } - - SafeTcpServerEventListener that = (SafeTcpServerEventListener) o; - - return !(delegate != null? !delegate.equals(that.delegate) : that.delegate != null); - - } - - @Override - public int hashCode() { - return delegate != null? delegate.hashCode() : 0; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/events/TcpServerEventListener.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/events/TcpServerEventListener.java deleted file mode 100644 index b3b6b37..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/events/TcpServerEventListener.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.server.events; - -import io.reactivex.netty.channel.events.ConnectionEventListener; -import io.reactivex.netty.protocol.tcp.server.TcpServer; - -import java.util.concurrent.TimeUnit; - -/** - * A listener for all events published by {@link TcpServer} - */ -public abstract class TcpServerEventListener extends ConnectionEventListener { - - /** - * Event whenever a new client connection is accepted. - */ - public void onNewClientConnected() { } - - /** - * Event when any connection handling starts. - * - * @param duration Time between a client connection is accepted to when it is handled by the connection handler. - * @param timeUnit Time unit for the duration. - */ - @SuppressWarnings("unused") - public void onConnectionHandlingStart(long duration, TimeUnit timeUnit) { } - - /** - * Event when any connection handling is successfully completed. - * - * @param duration Time taken for connection handling. - * @param timeUnit Time unit for the duration. - */ - @SuppressWarnings("unused") - public void onConnectionHandlingSuccess(long duration, TimeUnit timeUnit) {} - - /** - * Event when any connection handling completes with an error. - * - * @param duration Time taken for connection handling. - * @param timeUnit Time unit for the duration. - */ - @SuppressWarnings("unused") - public void onConnectionHandlingFailed(long duration, TimeUnit timeUnit, Throwable throwable) { } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/events/TcpServerEventPublisher.java b/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/events/TcpServerEventPublisher.java deleted file mode 100644 index a2f1526..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/protocol/tcp/server/events/TcpServerEventPublisher.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.server.events; - -import io.reactivex.netty.channel.events.ConnectionEventPublisher; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.events.EventSource; -import io.reactivex.netty.events.ListenersHolder; -import io.reactivex.netty.events.internal.SafeEventListener; -import rx.Subscription; -import rx.functions.Action1; -import rx.functions.Action3; -import rx.functions.Action4; -import rx.subscriptions.CompositeSubscription; - -import java.util.concurrent.TimeUnit; - -public final class TcpServerEventPublisher extends TcpServerEventListener - implements EventSource, EventPublisher { - - private static final Action1 NEW_CLIENT_ACTION = new Action1() { - @Override - public void call(TcpServerEventListener l) { - l.onNewClientConnected(); - } - }; - - private static final Action3 HANDLE_START_ACTION = - new Action3() { - @Override - public void call(TcpServerEventListener l, Long duration, TimeUnit timeUnit) { - l.onConnectionHandlingStart(duration, timeUnit); - } - }; - - private static final Action3 HANDLE_SUCCESS_ACTION = - new Action3() { - @Override - public void call(TcpServerEventListener l, Long duration, TimeUnit timeUnit) { - l.onConnectionHandlingSuccess(duration, timeUnit); - } - }; - - private static final Action4 HANDLE_FAILED_ACTION = - new Action4() { - @Override - public void call(TcpServerEventListener l, Long duration, TimeUnit timeUnit, Throwable t) { - l.onConnectionHandlingFailed(duration, timeUnit, t); - } - }; - - private final ListenersHolder listeners; - private final ConnectionEventPublisher connDelegate; - - public TcpServerEventPublisher() { - listeners = new ListenersHolder<>(); - connDelegate = new ConnectionEventPublisher<>(); - } - - private TcpServerEventPublisher(TcpServerEventPublisher toCopy) { - listeners = toCopy.listeners.copy(); - connDelegate = toCopy.connDelegate.copy(); - } - - @Override - public void onNewClientConnected() { - listeners.invokeListeners(NEW_CLIENT_ACTION); - } - - @Override - public void onConnectionHandlingStart(final long duration, final TimeUnit timeUnit) { - listeners.invokeListeners(HANDLE_START_ACTION, duration, timeUnit); - } - - @Override - public void onConnectionHandlingSuccess(final long duration, final TimeUnit timeUnit) { - listeners.invokeListeners(HANDLE_SUCCESS_ACTION, duration, timeUnit); - } - - @Override - public void onConnectionHandlingFailed(final long duration, final TimeUnit timeUnit, final Throwable throwable) { - listeners.invokeListeners(HANDLE_FAILED_ACTION, duration, timeUnit, throwable); - } - - @Override - public void onByteRead(long bytesRead) { - connDelegate.onByteRead(bytesRead); - } - - @Override - public void onByteWritten(long bytesWritten) { - connDelegate.onByteWritten(bytesWritten); - } - - @Override - public void onFlushStart() { - connDelegate.onFlushStart(); - } - - @Override - public void onFlushComplete(long duration, TimeUnit timeUnit) { - connDelegate.onFlushComplete(duration, timeUnit); - } - - @Override - public void onWriteStart() { - connDelegate.onWriteStart(); - } - - @Override - public void onWriteSuccess(long duration, TimeUnit timeUnit) { - connDelegate.onWriteSuccess(duration, timeUnit); - } - - @Override - public void onWriteFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - connDelegate.onWriteFailed(duration, timeUnit, throwable); - } - - @Override - public void onConnectionCloseStart() { - connDelegate.onConnectionCloseStart(); - } - - @Override - public void onConnectionCloseSuccess(long duration, TimeUnit timeUnit) { - connDelegate.onConnectionCloseSuccess(duration, timeUnit); - } - - @Override - public void onConnectionCloseFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - connDelegate.onConnectionCloseFailed(duration, timeUnit, throwable); - } - - @Override - public void onCustomEvent(Object event) { - connDelegate.onCustomEvent(event); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { - connDelegate.onCustomEvent(event, duration, timeUnit); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { - connDelegate.onCustomEvent(event, duration, timeUnit, throwable); - } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { - connDelegate.onCustomEvent(event, throwable); - } - - @Override - public boolean publishingEnabled() { - return listeners.publishingEnabled(); - } - - @Override - public Subscription subscribe(TcpServerEventListener listener) { - if (!SafeEventListener.class.isAssignableFrom(listener.getClass())) { - listener = new SafeTcpServerEventListener(listener); - } - - CompositeSubscription cs = new CompositeSubscription(); - cs.add(listeners.subscribe(listener)); - cs.add(connDelegate.subscribe(listener)); - return cs; - } - - public TcpServerEventPublisher copy() { - return new TcpServerEventPublisher(this); - } - - /*Visible for testing*/ListenersHolder getListeners() { - return listeners; - } - - /*Visible for testing*/ConnectionEventPublisher getConnDelegate() { - return connDelegate; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/server/ServerState.java b/netty-http-rx/src/main/java/io/reactivex/netty/server/ServerState.java deleted file mode 100644 index f83612e..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/server/ServerState.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.server; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.ServerChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.util.concurrent.EventExecutorGroup; -import io.reactivex.netty.HandlerNames; -import io.reactivex.netty.channel.DetachedChannelPipeline; -import io.reactivex.netty.channel.WriteTransformer; -import rx.functions.Action1; -import rx.functions.Func0; - -import java.net.SocketAddress; - -/** - * A collection of state that a server holds. - * - * @param The type of objects read from the server owning this state. - * @param The type of objects written to the server owning this state. - */ -public abstract class ServerState { - - protected final SocketAddress socketAddress; - protected final ServerBootstrap bootstrap; - protected final DetachedChannelPipeline detachedPipeline; - - protected ServerState(SocketAddress socketAddress, EventLoopGroup parent, EventLoopGroup child, - Class channelClass) { - this.socketAddress = socketAddress; - bootstrap = new ServerBootstrap(); - bootstrap.childOption(ChannelOption.AUTO_READ, false); // by default do not read content unless asked. - bootstrap.group(parent, child); - bootstrap.channel(channelClass); - detachedPipeline = new DetachedChannelPipeline(); - detachedPipeline.addLast(HandlerNames.WriteTransformer.getName(), new Func0() { - @Override - public ChannelHandler call() { - return new WriteTransformer(); - } - }); - bootstrap.childHandler(detachedPipeline.getChannelInitializer()); - } - - protected ServerState(ServerState toCopy, final ServerBootstrap newBootstrap) { - socketAddress = toCopy.socketAddress; - bootstrap = newBootstrap; - detachedPipeline = toCopy.detachedPipeline; - bootstrap.childHandler(detachedPipeline.getChannelInitializer()); - } - - protected ServerState(ServerState toCopy, final DetachedChannelPipeline newPipeline) { - final ServerState toCopyCast = toCopy.cast(); - socketAddress = toCopy.socketAddress; - bootstrap = toCopyCast.bootstrap.clone(); - detachedPipeline = newPipeline; - bootstrap.childHandler(detachedPipeline.getChannelInitializer()); - } - - protected ServerState(ServerState toCopy, final SocketAddress socketAddress) { - this.socketAddress = socketAddress; - bootstrap = toCopy.bootstrap.clone(); - detachedPipeline = toCopy.detachedPipeline; - bootstrap.childHandler(detachedPipeline.getChannelInitializer()); - } - - public ServerState channelOption(ChannelOption option, T value) { - ServerState copy = copyBootstrapOnly(); - copy.bootstrap.option(option, value); - return copy; - } - - public ServerState clientChannelOption(ChannelOption option, T value) { - ServerState copy = copyBootstrapOnly(); - copy.bootstrap.childOption(option, value); - return copy; - } - - public ServerState addChannelHandlerFirst(String name, Func0 handlerFactory) { - ServerState copy = copy(); - copy.detachedPipeline.addFirst(name, handlerFactory); - return copy; - } - - public ServerState addChannelHandlerFirst(EventExecutorGroup group, String name, - Func0 handlerFactory) { - ServerState copy = copy(); - copy.detachedPipeline.addFirst(group, name, handlerFactory); - return copy; - } - - public ServerState addChannelHandlerLast(String name, Func0 handlerFactory) { - ServerState copy = copy(); - copy.detachedPipeline.addLast(name, handlerFactory); - return copy; - } - - public ServerState addChannelHandlerLast(EventExecutorGroup group, String name, - Func0 handlerFactory) { - ServerState copy = copy(); - copy.detachedPipeline.addLast(group, name, handlerFactory); - return copy; - } - - public ServerState addChannelHandlerBefore(String baseName, String name, - Func0 handlerFactory) { - ServerState copy = copy(); - copy.detachedPipeline.addBefore(baseName, name, handlerFactory); - return copy; - } - - public ServerState addChannelHandlerBefore(EventExecutorGroup group, String baseName, - String name, Func0 handlerFactory) { - ServerState copy = copy(); - copy.detachedPipeline.addBefore(group, baseName, name, handlerFactory); - return copy; - } - - public ServerState addChannelHandlerAfter(String baseName, String name, - Func0 handlerFactory) { - ServerState copy = copy(); - copy.detachedPipeline.addAfter(baseName, name, handlerFactory); - return copy; - } - - public ServerState addChannelHandlerAfter(EventExecutorGroup group, String baseName, - String name, Func0 handlerFactory) { - ServerState copy = copy(); - copy.detachedPipeline.addAfter(group, baseName, name, handlerFactory); - return copy; - } - - public ServerState pipelineConfigurator(Action1 pipelineConfigurator) { - ServerState copy = copy(); - copy.detachedPipeline.configure(pipelineConfigurator); - return copy; - } - - public ServerState enableWireLogging(final LogLevel wireLoggingLevel) { - return enableWireLogging(LoggingHandler.class.getName(), wireLoggingLevel); - } - - public ServerState enableWireLogging(final String name, final LogLevel wireLoggingLevel) { - return addChannelHandlerFirst(HandlerNames.WireLogging.getName(), new Func0() { - @Override - public ChannelHandler call() { - return new LoggingHandler(name, wireLoggingLevel); - } - }); - } - - public ServerState serverAddress(SocketAddress socketAddress) { - return copy(socketAddress); - } - - public SocketAddress getServerAddress() { - return socketAddress; - } - - /*package private. Should not leak as it is mutable*/ ServerBootstrap getBootstrap() { - return bootstrap; - } - - protected abstract ServerState copyBootstrapOnly(); - - protected abstract ServerState copy(); - - protected abstract ServerState copy(SocketAddress newSocketAddress); - - @SuppressWarnings("unchecked") - private ServerState cast() { - return (ServerState) this; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/ssl/DefaultSslCodec.java b/netty-http-rx/src/main/java/io/reactivex/netty/ssl/DefaultSslCodec.java deleted file mode 100644 index 4b6fd5b..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/ssl/DefaultSslCodec.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.ssl; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.ssl.SslHandler; -import rx.functions.Func1; - -import javax.net.ssl.SSLEngine; - -/** - * Default implementation of {@link SslCodec} that uses a statically created {@link SSLEngine} or a provided factory to - * create one dynamically. - * - * No custom configurations are applied to the created {@link SslHandler} unless done so explicity by overriding - * {@link #configureHandler(SslHandler)} - */ -public class DefaultSslCodec extends SslCodec { - - private final Func1 engineFactory; - - public DefaultSslCodec(Func1 engineFactory) { - this.engineFactory = engineFactory; - } - - public DefaultSslCodec(final SSLEngine sslEngine) { - this(new Func1() { - @Override - public SSLEngine call(ByteBufAllocator allocator) { - return sslEngine; - } - }); - } - - @Override - protected SslHandler newSslHandler(ChannelPipeline pipeline) { - SslHandler toReturn = new SslHandler(engineFactory.call(pipeline.channel().alloc())); - configureHandler(toReturn); - return toReturn; - } - - /** - * An optional method that can be overridden to add any custom configurations to the {@link SslHandler} returned - * by {@link #newSslHandler(ChannelPipeline)} - * - * @param handler Handler to configure. - */ - protected void configureHandler(@SuppressWarnings("unused")SslHandler handler) { - // No Op .. - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/ssl/SslCodec.java b/netty-http-rx/src/main/java/io/reactivex/netty/ssl/SslCodec.java deleted file mode 100644 index a4b1483..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/ssl/SslCodec.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.ssl; - -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.ssl.SslHandler; -import io.netty.handler.ssl.SslHandshakeCompletionEvent; -import io.reactivex.netty.channel.ConnectionCreationFailedEvent; -import io.reactivex.netty.channel.EmitConnectionEvent; -import rx.functions.Action1; - -import static io.reactivex.netty.HandlerNames.*; - -/** - * A codec to use when enabling SSL/TLS on SSL - * · TLS and StartTLS support for a TCP client/server. - * - * This codec requires an {@link SslHandler} instance and adds the necessary infrastructure required for the - * TCP client/server to work. - */ -public abstract class SslCodec implements Action1 { - - protected SslCodec() { - } - - @Override - public final void call(final ChannelPipeline pipeline) { - final SslHandler sslHandler = newSslHandler(pipeline); - ChannelHandler wireLogging = pipeline.get(WireLogging.getName()); - if (null != wireLogging) { - /*So that, all activity on the channel is printed including SSL.*/ - pipeline.addAfter(WireLogging.getName(), SslHandler.getName(), sslHandler); - } else { - pipeline.addFirst(SslHandler.getName(), sslHandler); - } - - pipeline.addAfter(SslHandler.getName(), SslConnectionEmissionHandler.getName(), new SslConnEmissionHandler()); - } - - protected abstract SslHandler newSslHandler(ChannelPipeline pipeline); - - private static final class SslConnEmissionHandler extends ChannelDuplexHandler { - - private boolean handshakeDone; - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - ctx.read(); // Read till handshake is over, else handshake will never be done, without reading from channel. - super.channelActive(ctx); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - if (!handshakeDone) { - ctx.read(); /*Read till handshake over.*/ - } - super.channelReadComplete(ctx); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof SslHandshakeCompletionEvent) { - handshakeDone = true; - SslHandshakeCompletionEvent handshakeCompletionEvent = (SslHandshakeCompletionEvent) evt; - if (handshakeCompletionEvent.isSuccess()) { - ctx.fireUserEventTriggered(EmitConnectionEvent.INSTANCE); - } else { - ctx.fireUserEventTriggered(new ConnectionCreationFailedEvent(handshakeCompletionEvent.cause())); - } - } - super.userEventTriggered(ctx, evt); - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/threads/PreferCurrentEventLoopGroup.java b/netty-http-rx/src/main/java/io/reactivex/netty/threads/PreferCurrentEventLoopGroup.java deleted file mode 100644 index 8bc0ea0..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/threads/PreferCurrentEventLoopGroup.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.threads; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelPromise; -import io.netty.channel.EventLoop; -import io.netty.channel.EventLoopGroup; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.FastThreadLocal; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.ScheduledFuture; - -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * This {@link EventLoopGroup} can be used for clients that favors use of the "current" {@link EventLoop} for the - * outbound connection. A current {@link EventLoop} is determined by checking if the thread calling {@link #next()} is - * an eventloop instance belonging to this group. If so, the same instance is returned from {@link #next()} otherwise, - * the call to {@link #next()} is delegated to the actual {@link EventLoopGroup} passed to this instance. - * - * This is generally useful for applications that process a received request by calling some other downstream - * applications. If, during processing of these requests, there is no new thread introduced, then the {@link EventLoop} - * processing the received request will also execute the outbound request to another application. - * - * The above, although being subtle has benefits around removing queuing while writing data to any channel in the entire - * request processing. - */ -public class PreferCurrentEventLoopGroup implements EventLoopGroup { - - private final FastThreadLocal self = new FastThreadLocal<>(); - private final EventLoopGroup delegate; - - public PreferCurrentEventLoopGroup(EventLoopGroup delegate) { - this.delegate = delegate; - for (final EventExecutor child : delegate) { - child.submit(new Runnable() { - @Override - public void run() { - if (child instanceof EventLoop) { - self.set((EventLoop) child); - } - } - }); // Since this is an optimization, there is no need for us to wait for this task to finish. - } - } - - @Override - public EventLoop next() { - final EventLoop thisEventLoop = self.get(); - return null != thisEventLoop ? thisEventLoop : delegate.next(); - } - - @Override - public ChannelFuture register(Channel channel) { - return next().register(channel); - } - - @Override - public ChannelFuture register(ChannelPromise promise) { - return next().register(promise); - } - - @Deprecated - @Override - public ChannelFuture register(Channel channel, - ChannelPromise promise) { - return next().register(channel, promise); - } - - @Override - public boolean isShuttingDown() { - return delegate.isShuttingDown(); - } - - @Override - public Future shutdownGracefully() { - return delegate.shutdownGracefully(); - } - - @Override - public Future shutdownGracefully(long quietPeriod, long timeout, - TimeUnit unit) { - return delegate.shutdownGracefully(quietPeriod, timeout, unit); - } - - @Override - public Future terminationFuture() { - return delegate.terminationFuture(); - } - - @Override - @Deprecated - public void shutdown() { - delegate.shutdown(); - } - - @Override - @Deprecated - public List shutdownNow() { - return delegate.shutdownNow(); - } - - @Override - @Deprecated - public Iterator iterator() { - return delegate.iterator(); - } - - @Override - public Future submit(Runnable task) { - return next().submit(task); - } - - @Override - public Future submit(Runnable task, T result) { - return next().submit(task, result); - } - - @Override - public Future submit(Callable task) { - return next().submit(task); - } - - @Override - public ScheduledFuture schedule(Runnable command, long delay, - TimeUnit unit) { - return next().schedule(command, delay, unit); - } - - @Override - public ScheduledFuture schedule(Callable callable, - long delay, TimeUnit unit) { - return next().schedule(callable, delay, unit); - } - - @Override - public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, - long period, - TimeUnit unit) { - return next().scheduleAtFixedRate(command, initialDelay, period, unit); - } - - @Override - public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, - long delay, - TimeUnit unit) { - return next().scheduleWithFixedDelay(command, initialDelay, delay, unit); - } - - @Override - public boolean isShutdown() { - return delegate.isShutdown(); - } - - @Override - public boolean isTerminated() { - return delegate.isTerminated(); - } - - @Override - public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { - return delegate.awaitTermination(timeout, unit); - } - - @Override - public List> invokeAll( - Collection> tasks) throws InterruptedException { - return next().invokeAll(tasks); - } - - @Override - public List> invokeAll( - Collection> tasks, long timeout, - TimeUnit unit) throws InterruptedException { - return next().invokeAll(tasks, timeout, unit); - } - - @Override - public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { - return next().invokeAny(tasks); - } - - @Override - public T invokeAny(Collection> tasks, long timeout, - TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - return next().invokeAny(tasks, timeout, unit); - } - - @Override - public void execute(Runnable command) { - next().execute(command); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/threads/RxDefaultThreadFactory.java b/netty-http-rx/src/main/java/io/reactivex/netty/threads/RxDefaultThreadFactory.java deleted file mode 100644 index 4a173a8..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/threads/RxDefaultThreadFactory.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.threads; - -import io.netty.util.concurrent.DefaultThreadFactory; - -public class RxDefaultThreadFactory extends DefaultThreadFactory { - - public RxDefaultThreadFactory(String threadNamePrefix) { - super(threadNamePrefix, true); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/threads/RxEventLoopProvider.java b/netty-http-rx/src/main/java/io/reactivex/netty/threads/RxEventLoopProvider.java deleted file mode 100644 index 9490264..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/threads/RxEventLoopProvider.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.threads; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.EventLoopGroup; - -/** - * A provider for netty's {@link EventLoopGroup} to be used for RxNetty's clients and servers when they are not - * provided explicitly. - */ -public abstract class RxEventLoopProvider { - - /** - * The {@link EventLoopGroup} to be used by all client instances if it is not explicitly provided in the client. - * - * @return The {@link EventLoopGroup} to be used for all clients. - */ - public abstract EventLoopGroup globalClientEventLoop(); - - /** - * The {@link EventLoopGroup} to be used by all server instances if it is not explicitly provided. - * - * @return The {@link EventLoopGroup} to be used for all servers. - */ - public abstract EventLoopGroup globalServerEventLoop(); - - /** - * The {@link EventLoopGroup} to be used by all server instances as a parent eventloop group - * (First argument to this method: {@link ServerBootstrap#group(EventLoopGroup, EventLoopGroup)}), - * if it is not explicitly provided. - * - * @return The {@link EventLoopGroup} to be used for all servers. - */ - public abstract EventLoopGroup globalServerParentEventLoop(); - - /** - * The {@link EventLoopGroup} to be used by all client instances if it is not explicitly provided. - * - * @param nativeTransport {@code true} If the eventloop for native transport is to be returned (if configured) - * - * @return The {@link EventLoopGroup} to be used for all client. If {@code nativeTransport} was {@code true} then - * return the {@link EventLoopGroup} for native transport. - */ - public abstract EventLoopGroup globalClientEventLoop(boolean nativeTransport); - - /** - * The {@link EventLoopGroup} to be used by all server instances if it is not explicitly provided. - * - * @param nativeTransport {@code true} If the eventloop for native transport is to be returned (if configured) - * - * @return The {@link EventLoopGroup} to be used for all servers. If {@code nativeTransport} was {@code true} then - * return the {@link EventLoopGroup} for native transport. - */ - public abstract EventLoopGroup globalServerEventLoop(boolean nativeTransport); - - /** - * The {@link EventLoopGroup} to be used by all server instances as a parent eventloop group - * (First argument to this method: {@link ServerBootstrap#group(EventLoopGroup, EventLoopGroup)}), - * if it is not explicitly provided. - * - * @param nativeTransport {@code true} If the eventloop for native transport is to be returned (if configured) - * - * @return The {@link EventLoopGroup} to be used for all servers. If {@code nativeTransport} was {@code true} then - * return the {@link EventLoopGroup} for native transport. - */ - public abstract EventLoopGroup globalServerParentEventLoop(boolean nativeTransport); -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/threads/RxJavaEventloopScheduler.java b/netty-http-rx/src/main/java/io/reactivex/netty/threads/RxJavaEventloopScheduler.java deleted file mode 100644 index 98be1aa..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/threads/RxJavaEventloopScheduler.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.threads; - -import io.netty.channel.EventLoop; -import io.netty.channel.EventLoopGroup; -import io.netty.util.concurrent.Future; -import rx.Scheduler; -import rx.Subscription; -import rx.annotations.Beta; -import rx.functions.Action0; -import rx.internal.schedulers.EventLoopsScheduler; -import rx.internal.schedulers.ScheduledAction; -import rx.internal.util.SubscriptionList; -import rx.subscriptions.CompositeSubscription; -import rx.subscriptions.Subscriptions; - -import java.util.concurrent.TimeUnit; - -/** - * A scheduler that uses a provided {@link EventLoopGroup} instance to schedule tasks. This should typically be used as - * a computation scheduler or any other scheduler that do not schedule blocking tasks. - */ -@Beta -public class RxJavaEventloopScheduler extends Scheduler { - - private final EventLoopGroup eventLoopGroup; - - public RxJavaEventloopScheduler(EventLoopGroup eventLoopGroup) { - this.eventLoopGroup = eventLoopGroup; - } - - @Override - public Worker createWorker() { - final EventLoop eventLoop = eventLoopGroup.next(); - return new EventloopWorker(eventLoop); - } - - /** - * This code is more or less copied from RxJava's {@link EventLoopsScheduler} worker code. - **/ - /*Visible for testing*/static class EventloopWorker extends Worker { - - /** - * Why are there two subscription holders? - * - * The serial subscriptions are used for non-delayed schedules which are always executed (and hence removed) - * in order. Since SubscriptionList holds the subs as a linked list, removals are optimal for serial removes. - * OTOH, delayed schedules are executed (and hence removed) out of order and hence a CompositeSubscription, - * that stores the subs in a hash structure is more optimal for removals. - */ - private final SubscriptionList serial; - private final CompositeSubscription timed; - private final SubscriptionList both; - private final EventLoop eventLoop; - - public EventloopWorker(EventLoop eventLoop) { - this.eventLoop = eventLoop; - serial = new SubscriptionList(); - timed = new CompositeSubscription(); - both = new SubscriptionList(serial, timed); - } - - @Override - public Subscription schedule(final Action0 action) { - return schedule(action, 0, TimeUnit.DAYS); - } - - @Override - public Subscription schedule(final Action0 action, long delayTime, TimeUnit unit) { - - if (isUnsubscribed()) { - return Subscriptions.unsubscribed(); - } - - final ScheduledAction sa; - - if (delayTime <= 0) { - sa = new ScheduledAction(action, serial); - serial.add(sa); - } else { - sa = new ScheduledAction(action, timed); - timed.add(sa); - } - - final Future result = eventLoop.schedule(sa, delayTime, unit); - Subscription cancelFuture = Subscriptions.create(new Action0() { - @Override - public void call() { - result.cancel(false); - } - }); - sa.add(cancelFuture); /*An unsubscribe of the returned sub should cancel the future*/ - return sa; - } - - @Override - public void unsubscribe() { - both.unsubscribe(); - } - - @Override - public boolean isUnsubscribed() { - return both.isUnsubscribed(); - } - - public boolean hasScheduledSubscriptions() { - return serial.hasSubscriptions(); - } - - public boolean hasDelayScheduledSubscriptions() { - return timed.hasSubscriptions(); - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/threads/RxJavaNettyBasedSchedulersHook.java b/netty-http-rx/src/main/java/io/reactivex/netty/threads/RxJavaNettyBasedSchedulersHook.java deleted file mode 100644 index 36a533d..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/threads/RxJavaNettyBasedSchedulersHook.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.threads; - -import io.netty.channel.EventLoopGroup; -import rx.Scheduler; -import rx.annotations.Beta; -import rx.plugins.RxJavaSchedulersHook; -import rx.schedulers.Schedulers; - -/** - * A scheduler hook for RxJava, to override the computation scheduler, as retrieved via {@link Schedulers#computation()}, - * with a scheduler to use netty's {@link EventLoopGroup}. The computation scheduler implementation is as provided as an - * {@link RxJavaEventloopScheduler} instance.

- * - * This is to be used as

- {@code - RxJavaPlugins.getInstance().registerSchedulersHook(hook); - } - at startup. - */ -@Beta -public class RxJavaNettyBasedSchedulersHook extends RxJavaSchedulersHook { - - private final RxJavaEventloopScheduler computationScheduler; - - public RxJavaNettyBasedSchedulersHook(RxJavaEventloopScheduler computationScheduler) { - this.computationScheduler = computationScheduler; - } - - @Override - public Scheduler getComputationScheduler() { - return computationScheduler; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/threads/SingleNioLoopProvider.java b/netty-http-rx/src/main/java/io/reactivex/netty/threads/SingleNioLoopProvider.java deleted file mode 100644 index 55d7e1f..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/threads/SingleNioLoopProvider.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.threads; - -import io.netty.channel.EventLoopGroup; -import io.netty.channel.epoll.EpollEventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.reactivex.netty.RxNetty; - -import java.util.concurrent.atomic.AtomicReference; - -/** - * An implementation of {@link RxEventLoopProvider} that returns the same {@link EventLoopGroup} instance for both - * client and server. - */ -public class SingleNioLoopProvider extends RxEventLoopProvider { - - private final EventLoopGroup eventLoop; - private final EventLoopGroup clientEventLoop; - private final EventLoopGroup parentEventLoop; - private final AtomicReference nativeEventLoop; - private final AtomicReference nativeClientEventLoop; - private final AtomicReference nativeParentEventLoop; - private final int parentEventLoopCount; - private final int childEventLoopCount; - - public SingleNioLoopProvider() { - this(Runtime.getRuntime().availableProcessors()); - } - - public SingleNioLoopProvider(int threadCount) { - eventLoop = new NioEventLoopGroup(threadCount, new RxDefaultThreadFactory("rxnetty-nio-eventloop")); - clientEventLoop = new PreferCurrentEventLoopGroup(eventLoop); - parentEventLoop = eventLoop; - parentEventLoopCount = childEventLoopCount = threadCount; - nativeEventLoop = new AtomicReference<>(); - nativeClientEventLoop = new AtomicReference<>(); - nativeParentEventLoop = nativeEventLoop; - } - - public SingleNioLoopProvider(int parentEventLoopCount, int childEventLoopCount) { - this.parentEventLoopCount = parentEventLoopCount; - this.childEventLoopCount = childEventLoopCount; - parentEventLoop = new NioEventLoopGroup(parentEventLoopCount, - new RxDefaultThreadFactory("rxnetty-nio-eventloop")); - eventLoop = new NioEventLoopGroup(childEventLoopCount, new RxDefaultThreadFactory("rxnetty-nio-eventloop")); - clientEventLoop = new PreferCurrentEventLoopGroup(eventLoop); - nativeParentEventLoop = new AtomicReference<>(); - nativeEventLoop = new AtomicReference<>(); - nativeClientEventLoop = new AtomicReference<>(); - } - - @Override - public EventLoopGroup globalClientEventLoop() { - return clientEventLoop; - } - - @Override - public EventLoopGroup globalServerEventLoop() { - return eventLoop; - } - - @Override - public EventLoopGroup globalServerParentEventLoop() { - return parentEventLoop; - } - - @Override - public EventLoopGroup globalClientEventLoop(boolean nativeTransport) { - if (nativeTransport && RxNetty.isUsingNativeTransport()) { - return getNativeClientEventLoop(); - } - return globalClientEventLoop(); - } - - @Override - public EventLoopGroup globalServerEventLoop(boolean nativeTransport) { - if (nativeTransport && RxNetty.isUsingNativeTransport()) { - return getNativeEventLoop(); - } - return globalServerEventLoop(); - } - - @Override - public EventLoopGroup globalServerParentEventLoop(boolean nativeTransport) { - if (nativeTransport && RxNetty.isUsingNativeTransport()) { - return getNativeParentEventLoop(); - } - return globalServerParentEventLoop(); - } - - private EventLoopGroup getNativeParentEventLoop() { - if (nativeParentEventLoop == nativeEventLoop) { // Means using same event loop for acceptor and worker pool. - return getNativeEventLoop(); - } - - EventLoopGroup eventLoopGroup = nativeParentEventLoop.get(); - if (null == eventLoopGroup) { - EventLoopGroup newEventLoopGroup = new EpollEventLoopGroup(parentEventLoopCount, - new RxDefaultThreadFactory( "rxnetty-epoll-eventloop")); - if (!nativeParentEventLoop.compareAndSet(null, newEventLoopGroup)) { - newEventLoopGroup.shutdownGracefully(); - } - } - return nativeParentEventLoop.get(); - } - - private EventLoopGroup getNativeEventLoop() { - EventLoopGroup eventLoopGroup = nativeEventLoop.get(); - if (null == eventLoopGroup) { - EventLoopGroup newEventLoopGroup = new EpollEventLoopGroup(childEventLoopCount, - new RxDefaultThreadFactory( "rxnetty-epoll-eventloop")); - if (!nativeEventLoop.compareAndSet(null, newEventLoopGroup)) { - newEventLoopGroup.shutdownGracefully(); - } - } - return nativeEventLoop.get(); - } - - private EventLoopGroup getNativeClientEventLoop() { - EventLoopGroup eventLoopGroup = nativeClientEventLoop.get(); - if (null == eventLoopGroup) { - EventLoopGroup newEventLoopGroup = new EpollEventLoopGroup(childEventLoopCount, - new RxDefaultThreadFactory( "rxnetty-epoll-eventloop")); - newEventLoopGroup = new PreferCurrentEventLoopGroup(newEventLoopGroup); - if (!nativeClientEventLoop.compareAndSet(null, newEventLoopGroup)) { - newEventLoopGroup.shutdownGracefully(); - } - } - return nativeClientEventLoop.get(); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/util/CollectBytes.java b/netty-http-rx/src/main/java/io/reactivex/netty/util/CollectBytes.java deleted file mode 100644 index 793de08..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/util/CollectBytes.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.util; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import rx.Observable; -import rx.Observable.Transformer; -import rx.exceptions.OnErrorThrowable; -import rx.functions.Action2; -import rx.functions.Func0; - -/** - * An {@link Observable.Transformer} to collect a stream of {@link ByteBuf ByteBufs} into a single - * ByteBuf. On success the receiver must release the returned ByteBuf. - * On failure this will release all received ByteBufs. - *

- * This Transformer should not be used with {@link io.reactivex.netty.channel.ContentSource#autoRelease()} - * as this will release the underlying collected ByteBufs before the collection is complete. - */ -public class CollectBytes implements Transformer { - - private final int maxBytes; - - /** - * Collect all emitted ByteBufs into a single ByteBuf. This will return at most - * {@link Integer#MAX_VALUE} - * bytes. This is the upper limit of {@link ByteBuf#readableBytes()}. If more than - * Integer#MAX_VALUE bytes are received a {@link TooMuchDataException} will be emitted. - * {@link TooMuchDataException#getCause()} - * will contain an - * {@link OnErrorThrowable.OnNextValue} with the bytes accumulated before the exception - * was thrown. - */ - public static CollectBytes all() { - return upTo(Integer.MAX_VALUE); - } - - /** - * Collect all emitted ByteBufs into a single ByteBuf until maxBytes have - * been collected. If more than maxBytes are received this will unsubscribe from - * the upstream Observable and will emit a - * {@link TooMuchDataException}. {@link TooMuchDataException#getCause()} - * will contain an - * {@link OnErrorThrowable.OnNextValue} with the bytes accumulated before the exception - * was thrown. - * @param maxBytes the maximum number of bytes to read - * @throws IllegalArgumentException when maxBytes is negative - */ - public static CollectBytes upTo(int maxBytes) { - return new CollectBytes(maxBytes); - } - - private CollectBytes(int maxBytes) { - if (maxBytes < 0) { - throw new IllegalArgumentException("maxBytes must not be negative"); - } - this.maxBytes = maxBytes; - } - - @Override - public Observable call(Observable upstream) { - return upstream - .collect( - new Func0() { - @Override - public CompositeByteBuf call() { - return Unpooled.compositeBuffer(); - } - }, - new Action2() { - @Override - public void call(CompositeByteBuf collector, ByteBuf buf) { - long newLength = collector.readableBytes() + buf.readableBytes(); - if (newLength <= maxBytes) { - collector.addComponent(true, buf); - } else { - collector.release(); - buf.release(); - throw new TooMuchDataException("More than " + maxBytes + "B received"); - } - } - } - ) - .cast(ByteBuf.class); - } - - @SuppressWarnings("serial") - public static class TooMuchDataException extends RuntimeException { - public TooMuchDataException(String message) { - super(message); - } - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/util/LineReader.java b/netty-http-rx/src/main/java/io/reactivex/netty/util/LineReader.java deleted file mode 100644 index 0819e3d..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/util/LineReader.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.util; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandler; -import io.netty.util.ByteProcessor; - -import java.nio.charset.Charset; -import java.util.List; - -/** - * A utility class to be used with a channel handler to parse {@code ByteBuffer}s that contain strings terminated with - * a new line. This reader supports non-blocking incremental reads split across random places in the line.

- * - * This is not thread-safe and must only be called from a {@link ChannelHandler}. - */ -public class LineReader { - - public static final int DEFAULT_INITIAL_CAPACITY = 256; - - private static final ByteProcessor LINE_END_FINDER = new ByteProcessor() { - public static final char LF = 10; - - @Override - public boolean process(byte value) throws Exception { - char nextByte = (char) value; - return LF != nextByte; - } - }; - private ByteBuf incompleteBuffer; - private final int maxLineLength; - private final Charset encoding; - - public LineReader() { - this(Integer.MAX_VALUE, Charset.defaultCharset()); - } - - public LineReader(int maxLineLength, Charset encoding) { - this.maxLineLength = maxLineLength; - this.encoding = encoding; - } - - /** - * Reads the {@code in} buffer as much as it can and adds all the read lines into the {@code out} list.

- * If there is any outstanding data that is not read, it is stored into a temporary buffer and the next decode will - * prepend this data to the newly read data.

- * - * {@link #dispose()} must be called when the associated {@link ChannelHandler} is removed from the pipeline. - * - * @param in Buffer to decode. - * @param out List to add the read lines to. - * @param allocator Allocator to allocate new buffers, if required. - */ - public void decode(ByteBuf in, List out, ByteBufAllocator allocator) { - while (in.isReadable()) { - final int startIndex = in.readerIndex(); - - int lastReadIndex = in.forEachByte(LINE_END_FINDER); - - if (-1 == lastReadIndex) { - // Buffer end without line termination - if (null == incompleteBuffer) { - incompleteBuffer = allocator.buffer(DEFAULT_INITIAL_CAPACITY, maxLineLength); - } - - /*Add to the incomplete buffer*/ - incompleteBuffer.ensureWritable(in.readableBytes()); - incompleteBuffer.writeBytes(in); - } else { - ByteBuf lineBuf = in.readSlice(lastReadIndex - startIndex); - String line; - if (null != incompleteBuffer) { - line = incompleteBuffer.toString(encoding) + lineBuf.toString(encoding); - incompleteBuffer.release(); - incompleteBuffer = null; - } else { - line = lineBuf.toString(encoding); - } - out.add(line); - in.skipBytes(1); // Skip new line character. - } - } - } - - /** - * Same as {@link #decode(ByteBuf, List, ByteBufAllocator)} but it also produces the left-over buffer, even in - * absence of a line termination. - * - * {@link #dispose()} must be called when the associated {@link ChannelHandler} is removed from the pipeline. - * - * @param in Buffer to decode. - * @param out List to add the read lines to. - * @param allocator Allocator to allocate new buffers, if required. - */ - public void decodeLast(ByteBuf in, List out, ByteBufAllocator allocator) { - decode(in, out, allocator); - if (null != incompleteBuffer && incompleteBuffer.isReadable()) { - out.add(incompleteBuffer.toString(encoding)); - } - } - - /** - * Disposes any half-read data buffers. - */ - public void dispose() { - if (null != incompleteBuffer) { - incompleteBuffer.release(); - } - } - - public static boolean isLineDelimiter(char c) { - return c == '\r' || c == '\n'; - } - - /*Visible for testing*/ ByteBuf getIncompleteBuffer() { - return incompleteBuffer; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/util/LoggingHandlerFactory.java b/netty-http-rx/src/main/java/io/reactivex/netty/util/LoggingHandlerFactory.java deleted file mode 100644 index 946a916..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/util/LoggingHandlerFactory.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.util; - -import io.netty.channel.ChannelHandler; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import rx.functions.Func0; - -import java.util.EnumMap; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * {@link LoggingHandler} is a shaerable handler and hence need not be created for all channels. This factory - * manages a static map of log level to instance which can be used directly instead of creating a new factory per - * client. - */ -public class LoggingHandlerFactory implements Func0 { - - private static final ConcurrentMap> factories = - new ConcurrentHashMap<>(); - - private final LoggingHandler loggingHandler; - - private LoggingHandlerFactory(String name, LogLevel wireLoggingLevel) { - loggingHandler = new LoggingHandler(name, wireLoggingLevel); - } - - public static LoggingHandler get(String name, LogLevel logLevel) { - return getFactory(name, logLevel).loggingHandler; - } - - public static LoggingHandlerFactory getFactory(String name, LogLevel logLevel) { - EnumMap f = factories.get(name); - if (null == f) { - f = newEnumMap(name); - EnumMap existing = factories.putIfAbsent(name, f); - if (null != existing) { - f = existing; - } - } - return f.get(logLevel); - } - - @Override - public ChannelHandler call() { - return loggingHandler;/*logging handler is shareable.*/ - } - - private static EnumMap newEnumMap(String name) { - EnumMap toReturn = new EnumMap<>(LogLevel.class); - for (LogLevel logLevel : LogLevel.values()) { - toReturn.put(logLevel, new LoggingHandlerFactory(name, logLevel)); - } - return toReturn; - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/util/StringLineDecoder.java b/netty-http-rx/src/main/java/io/reactivex/netty/util/StringLineDecoder.java deleted file mode 100644 index 9f9f2b6..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/util/StringLineDecoder.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.util; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; - -import java.util.List; - -/** - * A decoder that breaks an incoming {@link ByteBuf}s into a list of strings delimited by a new line. - */ -public class StringLineDecoder extends ByteToMessageDecoder { - - private final LineReader lineReader = new LineReader(); - - @Override - protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { - lineReader.dispose(); - super.handlerRemoved0(ctx); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { - lineReader.decode(in, out, ctx.alloc()); - } -} diff --git a/netty-http-rx/src/main/java/io/reactivex/netty/util/UnicastBufferingSubject.java b/netty-http-rx/src/main/java/io/reactivex/netty/util/UnicastBufferingSubject.java deleted file mode 100644 index 19f5afa..0000000 --- a/netty-http-rx/src/main/java/io/reactivex/netty/util/UnicastBufferingSubject.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.util; - -import rx.Subscriber; -import rx.annotations.Experimental; -import rx.exceptions.Exceptions; -import rx.exceptions.MissingBackpressureException; -import rx.functions.Action0; -import rx.internal.util.BackpressureDrainManager; -import rx.internal.util.BackpressureDrainManager.BackpressureQueueCallback; -import rx.subjects.Subject; -import rx.subscriptions.Subscriptions; - -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicLong; - -/** - * An {@code Observable} that only supports a single active subscriber and buffers messages on back pressure and when - * there is no active subscription. - * - *

Add new messages

- * - * New messages can be added to this subject via - *
    -
  • {@link #onNext(Object)}: This throws an error if there is a buffer overflow.
  • -
  • {@link #offerNext(Object)}: This returns {@code false} if there is a buffer overflow.
  • -
- * - *

Backpressure

- * - * This subject supports backpressure from the only concurrent subscriber it can have at any time. The buffer limits - * that are specified while creating the subject is the maximum buffer that is allowed during backpressure. - * - * @param The type of objects accepted by this subject. - */ -@Experimental -public class UnicastBufferingSubject extends Subject { - - private final State state; - - protected UnicastBufferingSubject(OnSubscribe onSubscribe, State state) { - super(onSubscribe); - this.state = state; - } - - public static UnicastBufferingSubject create(long bufferSize) { - final State state = new State<>(bufferSize); - return new UnicastBufferingSubject<>(new OnSubscribe() { - @Override - public void call(Subscriber subscriber) { - state.registerSubscriber(subscriber); - } - }, state); - } - - public boolean isTerminated() { - synchronized (state) { - if (null != state.producer) { - return state.producer.isTerminated(); - } else { - return state.terminatedBeforeSubscribe; - } - } - } - - @Override - public boolean hasObservers() { - return null != state.subscriber; - } - - @Override - public void onCompleted() { - BackpressureDrainManager p = null; - /* If a subscriber is active, send the completion to the subscriber, else store it to be delivered to the - * buffered producer post subscription.*/ - synchronized (state) { - if (null != state.producer) { - p = state.producer; - } else { - state.terminatedBeforeSubscribe = true; - state.errorBeforeSubscribe = null; - } - } - - /*Send callbacks outside the sync block*/ - if (null != p) { - p.terminateAndDrain(); - } - } - - @Override - public void onError(Throwable e) { - BackpressureDrainManager p = null; - /* If a subscriber is active, send the completion to the subscriber, else store it to be delivered to the - * buffered producer post subscription.*/ - synchronized (state) { - if (null != state.producer) { - p = state.producer; - } else { - state.terminatedBeforeSubscribe = true; - state.errorBeforeSubscribe = e; - } - } - - /*Send callbacks outside the sync block*/ - if (null != p) { - p.terminateAndDrain(e); - } - } - - @Override - public void onNext(T t) { - try { - addNext(t); - } catch (MissingBackpressureException e) { - throw Exceptions.propagate(e); - } - } - - private void addNext(T next) throws MissingBackpressureException { - if (isTerminated()) { - throw new IllegalStateException("Observable is already completed."); - } - - /*Check for overflow*/ - while (true) { - final long currentSize = state.currentSize.get(); - final long newSize = currentSize + 1; - if (newSize > state.maxBufferedCount) { - throw new MissingBackpressureException("Max buffer limit exceeded. Current size: " + currentSize); - } - - if (state.currentSize.compareAndSet(currentSize, newSize)) { - break; - } - } - - state.nexts.add(next); - - BackpressureDrainManager p = null; - /*Drain the producer, if a subscriber is active.*/ - synchronized (state) { - if (null != state.producer) { - p = state.producer; - } - } - if (null != p) { - p.drain(); - } - } - - /** - * Offers the passed item to this subject. Same as {@link #onNext(Object)} just that this method does not throw an - * exception in case of buffer overflow, instead returns a {@code false}. - * - * @param next Next item to offer. - * - * @return {@code true} if the item was accepted, {@code false} if the subject is already terminated or the buffer - * is full. - */ - public boolean offerNext(T next) { - try { - addNext(next); - return true; - } catch (MissingBackpressureException e) { - return false; - } - } - - private static final class State { - - private final ConcurrentLinkedQueue nexts; - - private final BackpressureQueueCallbackImpl queueCallback; - private final AtomicLong currentSize = new AtomicLong(); - - private final long maxBufferedCount; - - private volatile Subscriber subscriber; - private volatile BackpressureDrainManager producer; - private volatile Throwable errorBeforeSubscribe; - private volatile boolean terminatedBeforeSubscribe; - - private State(long maxBufferedCount) { - this.maxBufferedCount = maxBufferedCount; - nexts = new ConcurrentLinkedQueue<>(); - queueCallback = new BackpressureQueueCallbackImpl(); - } - - public void registerSubscriber(final Subscriber subscriber) { - - boolean _shdSubscribe = false; - boolean _terminated = false; - Throwable _terminalError = null; - BackpressureDrainManager p = null; - synchronized (this) { - if (null == this.subscriber) { - this.subscriber = subscriber; - _shdSubscribe = true; - _terminated = terminatedBeforeSubscribe; - _terminalError = errorBeforeSubscribe; - p = new BackpressureDrainManager(queueCallback); - producer = p; - } - } - - if (_shdSubscribe) { - subscriber.add(Subscriptions.create(new Action0() { - @Override - public void call() { - synchronized (State.this) { - State.this.subscriber = null; - State.this.producer = null; - /** - * Why not clear the terminate-before-subscribe state? - * It can be so that there are multiple subscribers and the first subscriber did not - * completely consume the events and hence on unsubscribe clears the terminal state. - * The new subscriber will never get the terminal state in this case. - */ - } - } - })); - subscriber.setProducer(p); - if (_terminated) { - p.terminateAndDrain(_terminalError); - } - } else { - subscriber.onError(new IllegalStateException("Only one subscriber is allowed.")); - } - } - - /** - * Shared {@link BackpressureQueueCallback} for all producers (subscribers) as there is no state in this - * callback. - */ - private class BackpressureQueueCallbackImpl implements BackpressureQueueCallback { - - @Override - public Object peek() { - return nexts.peek(); - } - - @Override - public Object poll() { - T poll = nexts.poll(); - if (null != poll) { - currentSize.decrementAndGet(); - } - return poll; - } - - @Override - public boolean accept(Object next) { - @SuppressWarnings("unchecked") - T t = (T) next; - subscriber.onNext(t); - return false; - } - - @Override - public void complete(Throwable exception) { - if (null == exception) { - subscriber.onCompleted(); - } else { - subscriber.onError(exception); - } - } - } - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/channel/AbstractConnectionToChannelBridgeTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/channel/AbstractConnectionToChannelBridgeTest.java deleted file mode 100644 index e0d0628..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/channel/AbstractConnectionToChannelBridgeTest.java +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.reactivex.netty.channel.BackpressureManagingHandler.RequestReadIfRequiredEvent; -import io.reactivex.netty.channel.events.ConnectionEventListener; -import io.reactivex.netty.test.util.MockEventPublisher; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.observers.TestSubscriber; - -import static io.reactivex.netty.test.util.MockEventPublisher.*; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; - -public class AbstractConnectionToChannelBridgeTest { - - @Rule - public final ConnectionHandlerRule connectionHandlerRule = new ConnectionHandlerRule(); - - @Test(timeout = 60000) - public void testChannelActive() throws Exception { - ConnectionSubscriber subscriber = connectionHandlerRule.enableConnectionSubscriberAndAssert(false); - - connectionHandlerRule.activateConnectionAndAssert(subscriber); - - assertThat("Duplicate channel active event sent a notification", subscriber.getOnNextEvents(), hasSize(1)); - connectionHandlerRule.handler.channelActive(connectionHandlerRule.ctx); // duplicate event should not trigger onNext. - /*One item from activation*/ - assertThat("Duplicate channel active event sent a notification", subscriber.getOnNextEvents(), hasSize(1)); - } - - @Test(timeout = 60000) - public void testEagerContentSubscriptionFail() throws Exception { - connectionHandlerRule.channel.config().setAutoRead(true); // should mandate eager content subscription - ConnectionSubscriber subscriber = connectionHandlerRule.enableConnectionSubscriberAndAssert(false); - - connectionHandlerRule.activateConnectionAndAssert(subscriber); - - ConnectionInputSubscriber inputSubscriber = connectionHandlerRule.enableConnectionInputSubscriber(); - - subscriber.assertTerminalEvent(); - assertThat("Unexpected first notification kind.", inputSubscriber.getOnErrorEvents(), hasSize(1)); - - } - - @Test(timeout = 60000) - public void testEagerContentSubscriptionPass() throws Exception { - connectionHandlerRule.channel.config().setAutoRead(true); // should mandate eager content subscription - - ConnectionSubscriber subscriber = connectionHandlerRule.enableConnectionSubscriberAndAssert(true); - - connectionHandlerRule.activateConnectionAndAssert(subscriber); // eagerly subscribes to input. - ConnectionInputSubscriber inputSubscriber = subscriber.getInputSubscriber(); - - assertThat("Unexpected notifications count after channel active.", inputSubscriber.getOnNextEvents(), - hasSize(0)); - inputSubscriber.assertNoErrors(); - assertThat("Input subscriber is unsubscribed.", inputSubscriber.isUnsubscribed(), is(false)); - } - - @Test(timeout = 60000) - public void testLazyContentSubscription() throws Exception { - connectionHandlerRule.channel.config().setAutoRead(false); - ConnectionSubscriber subscriber = connectionHandlerRule.enableConnectionSubscriberAndAssert(false); //lazy input sub. - connectionHandlerRule.activateConnectionAndAssert(subscriber); - ConnectionInputSubscriber inputSubscriber = connectionHandlerRule.enableConnectionInputSubscriber(); - - inputSubscriber.assertNoErrors(); - assertThat("Unexpected on next events after channel active.", inputSubscriber.getOnNextEvents(), - hasSize(0)); - assertThat("Unexpected on completed events after channel active.", inputSubscriber.getOnCompletedEvents(), - hasSize(0)); - assertThat("Input subscriber is unsubscribed.", inputSubscriber.isUnsubscribed(), is(false)); - - connectionHandlerRule.startRead(); - connectionHandlerRule.testSendInputMsgs(inputSubscriber, "hello1"); - } - - @Test(timeout = 60000) - public void testInputCompleteOnChannelUnregister() throws Exception { - connectionHandlerRule.channel.config().setAutoRead(false); - ConnectionSubscriber subscriber = connectionHandlerRule.enableConnectionSubscriberAndAssert(true); - connectionHandlerRule.activateConnectionAndAssert(subscriber); - ConnectionInputSubscriber inputSubscriber = subscriber.getInputSubscriber(); // since sub is eager. - connectionHandlerRule.startRead(); - connectionHandlerRule.testSendInputMsgs(inputSubscriber, "hello1"); - - - assertThat("Unexpected notifications count after channel active.", inputSubscriber.getOnNextEvents(), - hasSize(1)); - inputSubscriber.unsubscribe(); // else channel close will generate error if subscribed - connectionHandlerRule.handler.channelUnregistered(connectionHandlerRule.ctx); - inputSubscriber.assertNoErrors(); - assertThat("Unexpected notifications count after channel active.", inputSubscriber.getOnNextEvents(), - hasSize(1)); - } - - @Test(timeout = 60000) - public void testMultipleInputSubscriptions() throws Exception { - connectionHandlerRule.channel.config().setAutoRead(false); - ConnectionSubscriber subscriber = connectionHandlerRule.enableConnectionSubscriberAndAssert(true); - connectionHandlerRule.activateConnectionAndAssert(subscriber); // one subscription - - ConnectionInputSubscriber inputSubscriber = connectionHandlerRule.enableConnectionInputSubscriber(); - - inputSubscriber.assertTerminalEvent(); - - assertThat("Unexpected on next events for second subscriber.", inputSubscriber.getOnNextEvents(), hasSize(0)); - assertThat("Unexpected notification type for second subscriber.", inputSubscriber.getOnErrorEvents(), - hasSize(1)); - } - - @Test(timeout = 60000) - public void testInputSubscriptionReset() throws Exception { - connectionHandlerRule.channel.config().setAutoRead(false); - ConnectionSubscriber subscriber = connectionHandlerRule.enableConnectionSubscriberAndAssert(true); - connectionHandlerRule.activateConnectionAndAssert(subscriber); // one subscription - - ConnectionInputSubscriber inputSubscriber = connectionHandlerRule.enableConnectionInputSubscriber(); - inputSubscriber.assertTerminalEvent(); - assertThat("Unexpected on next events for second subscriber.", inputSubscriber.getOnNextEvents(), hasSize(0)); - - connectionHandlerRule.handler.userEventTriggered(connectionHandlerRule.ctx, - new ConnectionInputSubscriberResetEvent() { - }); - - inputSubscriber = connectionHandlerRule.enableConnectionInputSubscriber(); - assertThat("Unexpected on next count for input subscriber post reset.", inputSubscriber.getOnNextEvents(), - hasSize(0)); - assertThat("Unexpected on error count for input subscriber post reset.", inputSubscriber.getOnErrorEvents(), - hasSize(0)); - assertThat("Unexpected on completed count for input subscriber post reset.", - inputSubscriber.getOnCompletedEvents(), hasSize(0)); - } - - @Test(timeout = 60000) - public void testErrorBeforeConnectionActive() throws Exception { - ConnectionSubscriber subscriber = connectionHandlerRule.enableConnectionSubscriberAndAssert(true); - final NullPointerException exception = new NullPointerException(); - connectionHandlerRule.handler.exceptionCaught(connectionHandlerRule.ctx, exception); - - subscriber.assertTerminalEvent(); - - assertThat("Unexpected on next notifications count post exception.", subscriber.getOnNextEvents(), hasSize(0)); - assertThat("Unexpected notification type post exception.", subscriber.getOnErrorEvents(), hasSize(1)); - } - - @Test(timeout = 60000) - public void testErrorPostInputSubscribe() throws Exception { - ConnectionSubscriber subscriber = connectionHandlerRule.enableConnectionSubscriberAndAssert(true); - connectionHandlerRule.activateConnectionAndAssert(subscriber); - ConnectionInputSubscriber inputSubscriber = subscriber.getInputSubscriber(); // since sub is eager. - - assertThat("Unexpected on next notifications count pre exception.", inputSubscriber.getOnNextEvents(), hasSize(0)); - assertThat("Unexpected on error notifications count pre exception.", inputSubscriber.getOnErrorEvents(), hasSize(0)); - assertThat("Unexpected on completed notifications count pre exception.", inputSubscriber.getOnCompletedEvents(), hasSize(0)); - final NullPointerException exception = new NullPointerException(); - connectionHandlerRule.handler.exceptionCaught(connectionHandlerRule.ctx, exception); - - inputSubscriber.assertTerminalEvent(); - - assertThat("Unexpected on next notifications count post exception.", inputSubscriber.getOnNextEvents(), hasSize(0)); - assertThat("Unexpected on error notifications count post exception.", inputSubscriber.getOnErrorEvents(), - hasSize(1)); - } - - public static class ConnectionHandlerRule extends ExternalResource { - - private Channel channel; - private ChannelHandlerContext ctx; - private AbstractConnectionToChannelBridge handler; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - channel = new EmbeddedChannel(new ChannelDuplexHandler()); - ctx = channel.pipeline().firstContext(); - handler = new AbstractConnectionToChannelBridge("foo", - new ConnectionEventListener() { }, - disabled()) { }; - base.evaluate(); - } - }; - } - - public void startRead() throws Exception { - handler.userEventTriggered(ctx, new RequestReadIfRequiredEvent() { - @Override - protected boolean shouldReadMore(ChannelHandlerContext ctx) { - return true; - } - }); - } - - public ConnectionSubscriber enableConnectionSubscriberAndAssert(boolean eagerSubToInput) throws Exception { - ConnectionSubscriber toReturn = new ConnectionSubscriber(eagerSubToInput, this); - handler.userEventTriggered(ctx, new ChannelSubscriberEvent<>(toReturn)); - assertThat("Unexpected on next notifications count before channel active.", toReturn.getOnNextEvents(), - hasSize(0)); - assertThat("Unexpected on error notifications count before channel active.", toReturn.getOnErrorEvents(), - hasSize(0)); - assertThat("Unexpected on complete notifications count before channel active.", toReturn.getOnCompletedEvents(), hasSize(0)); - return toReturn; - } - - public ConnectionInputSubscriber enableConnectionInputSubscriber() - throws Exception { - ConnectionInputSubscriber toReturn = new ConnectionInputSubscriber(); - handler.userEventTriggered(ctx, new ConnectionInputSubscriberEvent<>(toReturn)); - return toReturn; - } - - public void activateConnectionAndAssert(ConnectionSubscriber subscriber) throws Exception { - handler.userEventTriggered(ctx, EmitConnectionEvent.INSTANCE); - - subscriber.assertTerminalEvent(); - subscriber.assertNoErrors(); - - assertThat("No connections received.", subscriber.getOnNextEvents(), is(not(empty()))); - assertThat("Unexpected channel in new connection.", subscriber.getOnNextEvents().get(0), - is(channel)); - - } - - public void testSendInputMsgs(ConnectionInputSubscriber inputSubscriber, String... msgs) throws Exception { - - for (String msg: msgs) { - handler.channelRead(ctx, msg); - } - - assertThat("Unexpected notifications count after read.", inputSubscriber.getOnNextEvents(), - hasSize(msgs.length)); - assertThat("Unexpected notifications count after read.", inputSubscriber.getOnNextEvents(), - contains(msgs)); - - assertThat("Input subscriber is unsubscribed after read.", inputSubscriber.isUnsubscribed(), - is(false)); - } - } - - public static class ConnectionSubscriber extends TestSubscriber { - - private final boolean subscribeToInput; - private final ConnectionHandlerRule rule; - private ConnectionInputSubscriber inputSubscriber; - - public ConnectionSubscriber(boolean subscribeToInput, ConnectionHandlerRule rule) { - this.subscribeToInput = subscribeToInput; - this.rule = rule; - } - - @Override - public void onNext(Channel channel) { - super.onNext(channel); - try { - if (subscribeToInput) { - inputSubscriber = rule.enableConnectionInputSubscriber(); - } - } catch (Exception e) { - onError(e); - } - } - - public ConnectionInputSubscriber getInputSubscriber() { - return inputSubscriber; - } - } - - public static class ConnectionInputSubscriber extends TestSubscriber { - - private final long requestAtStart; - - public ConnectionInputSubscriber() { - this(Long.MAX_VALUE); - } - - public ConnectionInputSubscriber(long requestAtStart) { - this.requestAtStart = requestAtStart; - } - - @Override - public void onStart() { - request(requestAtStart); - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/channel/BackpressureManagingHandlerTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/channel/BackpressureManagingHandlerTest.java deleted file mode 100644 index d750469..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/channel/BackpressureManagingHandlerTest.java +++ /dev/null @@ -1,458 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundHandlerAdapter; -import io.netty.channel.ChannelPromise; -import io.netty.channel.FixedRecvByteBufAllocator; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.logging.LoggingHandler; -import io.reactivex.netty.channel.BackpressureManagingHandler.BytesWriteInterceptor; -import io.reactivex.netty.channel.BackpressureManagingHandler.RequestReadIfRequiredEvent; -import io.reactivex.netty.channel.BackpressureManagingHandler.State; -import io.reactivex.netty.test.util.InboundRequestFeeder; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.mockito.Mockito; -import rx.Observable; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicLong; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class BackpressureManagingHandlerTest { - - @Rule - public final HandlerRule handlerRule = new HandlerRule(); - - @Test(timeout = 60000) - public void testExactDemandAndSupply() throws Exception { - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - - final String msg1 = "hello1"; - final String msg2 = "hello2"; - handlerRule.feedMessagesForRead(msg1, msg2); /*Exact supply*/ - - handlerRule.setMaxMessagesPerRead(2); /*Send all msgs in one iteration*/ - handlerRule.requestMessages(2); /*Exact demand*/ - - assertThat("Unexpected read requested count.", handlerRule.getReadRequestedCount(), is(1)); - handlerRule.assertMessagesReceived(msg1, msg2); - - /*Since, the demand is met (requested 2 and got 2) , we move to buffering.*/ - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - } - - @Test(timeout = 60000) - public void testExactDemandAndSupplyMultiRequests() throws Exception { - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - - final String msg1 = "hello1"; - final String msg2 = "hello2"; - handlerRule.feedMessagesForRead(msg1, msg2); /*Exact supply*/ - - handlerRule.setMaxMessagesPerRead(2); /*Send all msgs in one iteration*/ - handlerRule.requestMessages(2); /*Exact demand*/ - - assertThat("Unexpected read requested count.", handlerRule.getReadRequestedCount(), is(1)); - handlerRule.assertMessagesReceived(msg1, msg2); - - /*Since, the demand is met (requested 2 and got 2) , we move to buffering.*/ - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - - handlerRule.resetReadCount(); - assertThat("Unexpected read requested count post reset.", handlerRule.getReadRequestedCount(), is(0)); - - handlerRule.handler.reset(); - - final String msg3 = "hello3"; - handlerRule.feedMessagesForRead(msg3); - - /*No demand, no read fired*/ - assertThat("Unexpected read requested count post reset.", handlerRule.getReadRequestedCount(), is(0)); - handlerRule.assertMessagesReceived(); - - handlerRule.requestMessages(1); - - /*Read on demand*/ - assertThat("Unexpected read requested count.", handlerRule.getReadRequestedCount(), is(1)); - handlerRule.assertMessagesReceived(msg3); - - /*Since, the demand is met (requested 3 and got 3) , we move to buffering.*/ - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Reading)); - } - - @Test(timeout = 60000) - public void testMoreDemand() throws Exception { - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - - final String msg1 = "hello1"; - final String msg2 = "hello2"; - handlerRule.feedMessagesForRead(msg1, msg2); /*less supply*/ - - handlerRule.setMaxMessagesPerRead(2); /*Send all msgs in one iteration*/ - handlerRule.requestMessages(4); /*More demand*/ - - /*One read for start and one when the supply completed but demand exists.*/ - assertThat("Unexpected read requested count.", handlerRule.getReadRequestedCount(), is(2)); - handlerRule.assertMessagesReceived(msg1, msg2); - - /*Since, the demand is not met (requested 4 but got 2) , stay in read requested.*/ - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.ReadRequested)); - } - - @Test(timeout = 60000) - public void testMoreSupply() throws Exception { - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - - final String msg1 = "hello1"; - final String msg2 = "hello2"; - final String msg3 = "hello3"; - handlerRule.feedMessagesForRead(msg1, msg2, msg3); /*more supply*/ - - handlerRule.setMaxMessagesPerRead(3); /*Send all msgs in one iteration*/ - handlerRule.requestMessages(2); /*less demand*/ - - /*One read for start.*/ - assertThat("Unexpected read requested count.", handlerRule.getReadRequestedCount(), is(1)); - handlerRule.assertMessagesReceived(msg1, msg2); - - /*Since, the demand was met (requested 2 and got 2) , but the supply was more (3), we should be buffering.*/ - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - assertThat("Unexpected buffer size.", handlerRule.handler.getBuffer(), hasSize(1)); - assertThat("Unexpected buffer contents.", handlerRule.handler.getBuffer(), contains((Object) msg3)); - assertThat("Unexpected buffer read index.", handlerRule.handler.getCurrentBufferIndex(), is(0)); - } - - @Test(timeout = 60000) - public void testBufferDrainSingleIteration() throws Exception { - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - - final String msg1 = "hello1"; - final String msg2 = "hello2"; - final String msg3 = "hello3"; - handlerRule.feedMessagesForRead(msg1, msg2, msg3); /*more supply*/ - - handlerRule.setMaxMessagesPerRead(3); /*Send all msgs in one iteration & cause buffer*/ - handlerRule.requestMessages(2); /*less demand*/ - - /*One read for start.*/ - assertThat("Unexpected read requested count.", handlerRule.getReadRequestedCount(), is(1)); - handlerRule.assertMessagesReceived(msg1, msg2); - - /*Since, the demand was met (requested 2 and got 2) , but the supply was more (3), we should be buffering.*/ - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - assertThat("Unexpected buffer size.", handlerRule.handler.getBuffer(), hasSize(1)); - assertThat("Unexpected buffer contents.", handlerRule.handler.getBuffer(), contains((Object) msg3)); - assertThat("Unexpected buffer read index.", handlerRule.handler.getCurrentBufferIndex(), is(0)); - - handlerRule.resetReadCount(); - assertThat("Unexpected read requested count post reset.", handlerRule.getReadRequestedCount(), is(0)); - - handlerRule.handler.reset(); - - handlerRule.requestMessages(1); /*Should come from the buffer.*/ - - assertThat("Unexpected read requested when expected to be fed from buffer.", - handlerRule.getReadRequestedCount(), is(0)); - handlerRule.assertMessagesReceived(msg3); - - /*Since, the demand is now met (requested 3 and got 3) , we move to buffering.*/ - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - assertThat("Unexpected buffer size.", handlerRule.handler.getBuffer(), is(nullValue())); - assertThat("Unexpected buffer read index.", handlerRule.handler.getCurrentBufferIndex(), is(0)); - } - - @Test(timeout = 60000) - public void testBufferDrainMultiIteration() throws Exception { - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - - final String msg1 = "hello1"; - final String msg2 = "hello2"; - final String msg3 = "hello3"; - final String msg4 = "hello4"; - handlerRule.feedMessagesForRead(msg1, msg2, msg3, msg4); /*more supply*/ - - handlerRule.setMaxMessagesPerRead(4); /*Send all msgs in one iteration & cause buffer*/ - handlerRule.requestMessages(2); /*less demand*/ - - /*One read for start.*/ - assertThat("Unexpected read requested count.", handlerRule.getReadRequestedCount(), is(1)); - handlerRule.assertMessagesReceived(msg1, msg2); - - /*Since, the demand was met (requested 2 and got 2) , but the supply was more (4), we should be buffering.*/ - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - assertThat("Unexpected buffer size.", handlerRule.handler.getBuffer(), hasSize(2)); - assertThat("Unexpected buffer contents.", handlerRule.handler.getBuffer(), contains((Object) msg3, msg4)); - assertThat("Unexpected buffer read index.", handlerRule.handler.getCurrentBufferIndex(), is(0)); - - /*Reset read state before next read*/ - handlerRule.resetReadCount(); - assertThat("Unexpected read requested count post reset.", handlerRule.getReadRequestedCount(), is(0)); - handlerRule.handler.reset(); - - handlerRule.requestMessages(1); /*Should come from the buffer.*/ - - assertThat("Unexpected read requested when expected to be fed from buffer.", - handlerRule.getReadRequestedCount(), is(0)); - handlerRule.assertMessagesReceived(msg3); - - /*Since, the demand is now met (requested 3 and got 3) , we move to buffering.*/ - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - /*Buffer does not change till it has data*/ - assertThat("Unexpected buffer size.", handlerRule.handler.getBuffer(), hasSize(2)); - /*Buffer reader index changes till it has data*/ - assertThat("Unexpected buffer read index.", handlerRule.handler.getCurrentBufferIndex(), is(1)); - - /*Reset read state before next read*/ - handlerRule.resetReadCount(); - assertThat("Unexpected read requested count post reset.", handlerRule.getReadRequestedCount(), is(0)); - handlerRule.handler.reset(); - - handlerRule.requestMessages(1); /*Should come from the buffer.*/ - - assertThat("Unexpected read requested when expected to be fed from buffer.", - handlerRule.getReadRequestedCount(), is(0)); - handlerRule.assertMessagesReceived(msg4); - - /*Since, the demand is now met (requested 4 and got 4) , we move to buffering.*/ - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - assertThat("Unexpected buffer size.", handlerRule.handler.getBuffer(), is(nullValue())); - assertThat("Unexpected buffer read index.", handlerRule.handler.getCurrentBufferIndex(), is(0)); - } - - @Test(timeout = 60000) - public void testBufferDrainWithMoreDemand() throws Exception { - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - - final String msg1 = "hello1"; - final String msg2 = "hello2"; - final String msg3 = "hello3"; - handlerRule.feedMessagesForRead(msg1, msg2, msg3); /*more supply*/ - - handlerRule.setMaxMessagesPerRead(3); /*Send all msgs in one iteration & cause buffer*/ - handlerRule.requestMessages(2); /*less demand*/ - - /*One read for start.*/ - assertThat("Unexpected read requested count.", handlerRule.getReadRequestedCount(), is(1)); - handlerRule.assertMessagesReceived(msg1, msg2); - - /*Since, the demand was met (requested 2 and got 2) , but the supply was more (3), we should be buffering.*/ - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - assertThat("Unexpected buffer size.", handlerRule.handler.getBuffer(), hasSize(1)); - assertThat("Unexpected buffer contents.", handlerRule.handler.getBuffer(), contains((Object) msg3)); - assertThat("Unexpected buffer read index.", handlerRule.handler.getCurrentBufferIndex(), is(0)); - - handlerRule.resetReadCount(); - assertThat("Unexpected read requested count post reset.", handlerRule.getReadRequestedCount(), is(0)); - - handlerRule.handler.reset(); - - handlerRule.requestMessages(2); /*Should come from the buffer.*/ - - /*Since demand can not be fulfilled by the buffer, a read should be requested.*/ - assertThat("Unexpected read requested.", handlerRule.getReadRequestedCount(), is(1)); - handlerRule.assertMessagesReceived(msg3); - - /*Since, the demand is now met (requested 3 and got 3) , we move to buffering.*/ - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.ReadRequested)); - assertThat("Unexpected buffer size.", handlerRule.handler.getBuffer(), is(nullValue())); - assertThat("Unexpected buffer read index.", handlerRule.handler.getCurrentBufferIndex(), is(0)); - } - - @Test(timeout = 60000) - public void testBufferDrainOnRemove() throws Exception { - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - - final ByteBuf msg1 = Unpooled.buffer().writeBytes("hello1".getBytes()); - final ByteBuf msg2 = Unpooled.buffer().writeBytes("hello2".getBytes()); - handlerRule.feedMessagesForRead(msg1, msg2); /*More supply then demand*/ - - handlerRule.setMaxMessagesPerRead(2); /*Send all msgs in one iteration and cause buffer*/ - handlerRule.requestMessages(1); /*Less demand*/ - - assertThat("Unexpected read requested count.", handlerRule.getReadRequestedCount(), is(1)); - handlerRule.assertMessagesReceived(msg1); - - /*Since, the demand is met (requested 1 and got 1) , we move to buffering.*/ - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - assertThat("Unexpected buffer size.", handlerRule.handler.getBuffer(), hasSize(1)); - assertThat("Unexpected buffer contents.", handlerRule.handler.getBuffer(), contains((Object) msg2)); - assertThat("Unexpected buffer read index.", handlerRule.handler.getCurrentBufferIndex(), is(0)); - - handlerRule.channel.close(); // Should remove handler. - handlerRule.channel.runPendingTasks(); - - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Stopped)); - assertThat("Unexpected buffer size.", handlerRule.handler.getBuffer(), is(nullValue())); - assertThat("Unexpected buffer read index.", handlerRule.handler.getCurrentBufferIndex(), is(0)); - assertThat("Buffered item not released.", msg2.refCnt(), is(0)); - } - - @Test(timeout = 60000) - public void testDiscardReadWhenStopped() throws Exception { - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Buffering)); - - handlerRule.channel.close(); // Should remove handler. - handlerRule.channel.runPendingTasks(); - - assertThat("Unexpected handler state.", handlerRule.handler.getCurrentState(), is(State.Stopped)); - - final ByteBuf msg = Unpooled.buffer().writeBytes("Hello".getBytes()); - handlerRule.handler.channelRead(Mockito.mock(ChannelHandlerContext.class), msg); - - assertThat("Message not released when stopped.", msg.refCnt(), is(0)); - } - - @Test(timeout = 60000) - public void testWriteWithBufferingHandler() throws Exception { - BufferingHandler bufferingHandler = new BufferingHandler(); - handlerRule.channel.pipeline() - .addBefore(BytesWriteInterceptor.WRITE_INSPECTOR_HANDLER_NAME, "buffering-handler", - bufferingHandler); - - final String[] dataToWrite = {"Hello1", "Hello2"}; - - handlerRule.channel.writeAndFlush(Observable.from(dataToWrite));/*Using Observable.from() to enable backpressure.*/ - - assertThat("Messages written to the channel, inspite of buffering", handlerRule.channel.outboundMessages(), - is(empty())); - - /*Inspite of the messages, not reaching the channel, the extra demand should be generated and the buffering - handler should contain all messages.*/ - assertThat("Unexpected buffer size in buffering handler.", bufferingHandler.buffer, hasSize(2)); - } - - public static class HandlerRule extends ExternalResource { - - private MockBackpressureManagingHandler handler; - private EmbeddedChannel channel; - private InboundRequestFeeder inboundRequestFeeder; - private FixedRecvByteBufAllocator recvByteBufAllocator; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - inboundRequestFeeder = new InboundRequestFeeder(); - channel = new EmbeddedChannel(new LoggingHandler()); - String bpName = "backpressure-manager"; - channel.pipeline().addFirst(bpName, - handler = new MockBackpressureManagingHandler(bpName)); - channel.pipeline().addBefore(bpName, "primitive-converter", new WriteTransformer()); - channel.pipeline().addFirst(inboundRequestFeeder); - channel.config().setAutoRead(false); - recvByteBufAllocator = new FixedRecvByteBufAllocator(1024); - channel.config().setRecvByteBufAllocator(recvByteBufAllocator); - base.evaluate(); - } - }; - } - - public void setMaxMessagesPerRead(int maxMessagesPerRead) { - recvByteBufAllocator.maxMessagesPerRead(maxMessagesPerRead); - } - - public void assertMessagesReceived(Object... expected) { - final List msgsReceived = handler.getMsgsReceived(); - - if (null != expected && expected.length > 0) { - assertThat("Unexpected messages received count.", msgsReceived, hasSize(expected.length)); - assertThat("Unexpected messages received.", msgsReceived, contains(expected)); - } else { - assertThat("Unexpected messages received.", msgsReceived, is(empty())); - } - } - - public int resetReadCount() { - return inboundRequestFeeder.resetReadRequested(); - } - - public int getReadRequestedCount() { - return inboundRequestFeeder.getReadRequestedCount(); - } - - public void requestMessages(long requested) throws Exception { - handler.incrementRequested(requested); - channel.pipeline().fireUserEventTriggered(new RequestReadIfRequiredEvent() { - @Override - protected boolean shouldReadMore(ChannelHandlerContext ctx) { - return true; - } - }); - channel.runPendingTasks(); - } - - public void feedMessagesForRead(Object... msgs) { - inboundRequestFeeder.addToTheFeed(msgs); - } - } - - private static class MockBackpressureManagingHandler extends BackpressureManagingHandler { - - private final List msgsReceived = new ArrayList<>(); - private final AtomicLong requested = new AtomicLong(); - - protected MockBackpressureManagingHandler(String thisHandlerName) { - super(thisHandlerName); - } - - @Override - protected void newMessage(ChannelHandlerContext ctx, Object msg) { - requested.decrementAndGet(); - msgsReceived.add(msg); - } - - @Override - protected boolean shouldReadMore(ChannelHandlerContext ctx) { - return requested.get() > 0; - } - - public List getMsgsReceived() { - return msgsReceived; - } - - public void reset() { - msgsReceived.clear(); - requested.set(0); - } - - public void incrementRequested(long requested) { - this.requested.addAndGet(requested); - } - } - - private static class BufferingHandler extends ChannelOutboundHandlerAdapter { - - private final List buffer = new ArrayList<>(); - - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - buffer.add(msg); - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/channel/BytesWriteInterceptorTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/channel/BytesWriteInterceptorTest.java deleted file mode 100644 index a1fdcfe..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/channel/BytesWriteInterceptorTest.java +++ /dev/null @@ -1,425 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.*; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.concurrent.AbstractScheduledEventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.ObjectUtil; -import io.reactivex.netty.channel.BackpressureManagingHandler.BytesWriteInterceptor; -import io.reactivex.netty.channel.BackpressureManagingHandler.WriteStreamSubscriber; -import io.reactivex.netty.test.util.MockProducer; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.Scheduler; -import rx.functions.Action0; -import rx.schedulers.Schedulers; - -import java.nio.charset.Charset; -import java.util.ArrayDeque; -import java.util.Queue; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import static io.reactivex.netty.channel.BackpressureManagingHandler.BytesWriteInterceptor.MAX_PER_SUBSCRIBER_REQUEST; -import static io.reactivex.netty.channel.BytesWriteInterceptorTest.InspectorRule.defaultRequestN; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; -import static rx.Observable.just; - -public class BytesWriteInterceptorTest { - - @Rule - public final InspectorRule inspectorRule = new InspectorRule(); - - @Test(timeout = 60000) - public void testAddSubscriber() throws Exception { - WriteStreamSubscriber sub1 = inspectorRule.newSubscriber(); - - assertThat("Subscriber not added.", inspectorRule.interceptor.getSubscribers(), hasSize(1)); - assertThat("Subscriber not added.", inspectorRule.interceptor.getSubscribers(), contains(sub1)); - - sub1.unsubscribe(); - inspectorRule.channel.runPendingTasks(); - assertThat("Subscriber not removed post unsubscribe", inspectorRule.interceptor.getSubscribers(), is(empty())); - } - - @Test(timeout = 60000) - public void testRequestMore() throws Exception { - - WriteStreamSubscriber sub1 = inspectorRule.newSubscriber(); - MockProducer mockProducer = inspectorRule.setupSubscriberAndValidate(sub1, 1); - assertThat("Unexpected items requested from producer.", mockProducer.getRequested(), is(defaultRequestN())); - - inspectorRule.sendMessages(1); - - assertThat("Channel not writable post write.", inspectorRule.channel.isWritable(), is(true)); - assertThat("Unexpected items requested.", mockProducer.getRequested(), is(defaultRequestN())); - } - - @Test(timeout = 60000) - public void testRequestMorePostFlush() throws Exception { - - WriteStreamSubscriber sub1 = inspectorRule.newSubscriber(); - MockProducer mockProducer = inspectorRule.setupSubscriberAndValidate(sub1, 1); - assertThat("Unexpected items requested from producer.", mockProducer.getRequested(), is(defaultRequestN())); - - inspectorRule.channel.config().setWriteBufferWaterMark(new WriteBufferWaterMark(1, 2)); /*Make sure that the channel is not writable on writing.*/ - - String msg = "Hello"; - inspectorRule.channel.write(msg); - - assertThat("Channel still writable.", inspectorRule.channel.isWritable(), is(false)); - assertThat("More items requested when channel is not writable.", mockProducer.getRequested(), - is(defaultRequestN())); - - inspectorRule.channel.flush(); - - assertThat("Channel not writable post flush.", inspectorRule.channel.isWritable(), is(true)); - assertThat("Unexpected items requested.", mockProducer.getRequested(), is(defaultRequestN())); - } - - @Test(timeout = 60000) - public void testMultiSubscribers() throws Exception { - WriteStreamSubscriber sub1 = inspectorRule.newSubscriber(); - MockProducer producer1 = inspectorRule.setupSubscriberAndValidate(sub1, 1); - - WriteStreamSubscriber sub2 = inspectorRule.newSubscriber(); - MockProducer producer2 = inspectorRule.setupSubscriberAndValidate(sub2, 2); - - inspectorRule.sendMessages(1); - - assertThat("Channel not writable post write.", inspectorRule.channel.isWritable(), is(true)); - assertThat("Unexpected items requested from first subscriber.", producer1.getRequested(), - is(defaultRequestN())); - assertThat("Unexpected items requested from second subscriber.", producer2.getRequested(), - is(defaultRequestN() / 2)); - } - - @Test(timeout = 10000) - public void testOneLongWriteAndManySmallWrites() throws Exception { - WriteStreamSubscriber sub1 = inspectorRule.newSubscriber(); - MockProducer producer1 = inspectorRule.setupSubscriberAndValidate(sub1, 1); - assertThat("Unexpected items requested from producer.", producer1.getRequested(), is(defaultRequestN())); - inspectorRule.setupNewSubscriberAndComplete(2, true); - inspectorRule.setupNewSubscriberAndComplete(2, true); - - inspectorRule.sendMessages(sub1, 33); - assertThat("Unexpected items requested.", producer1.getRequested(), is(97L)); - } - - @Ignore - @Test(timeout = 100000) - public void testWritesInOrderFromDifferentThreads() throws Exception { - final WriteStreamSubscriber sub1 = inspectorRule.newSubscriber(); - - // Set the current thread to be the thread of the event loop - inspectorRule.setEventLoopThread(); - - // Send 1000 messages from two different threads - int msgCount = 1000; - Scheduler.Worker worker = Schedulers.computation().createWorker(); - for (int i = 1; i < msgCount; i+=2) { - sub1.onNext(String.valueOf(i)); - - // Send from other thread - inspectorRule.sendFromOtherThread(sub1, worker, String.valueOf(i+1)); - } - - // In lack of a way of running all pending tasks on computation scheduler - Thread.sleep(1000); - - // Ensure messages are in order - Queue written = inspectorRule.getWrittenMessages(); - for (int i = 1; i <= msgCount; i++) { - Object msg = written.poll(); - String strMsg = ((ByteBuf) msg).toString(Charset.defaultCharset()); - assertThat("Not in order ", strMsg, is(String.valueOf(i))); - } - } - - @Test(timeout = 10000) - public void testBatchedSubscriberRemoves() { - WriteStreamSubscriber sub1 = inspectorRule.newSubscriber(); - MockProducer producer1 = inspectorRule.setupSubscriberAndValidate(sub1, 1); - assertThat("Unexpected items requested from producer.", producer1.getRequested(), is(defaultRequestN())); - for (int i=1; i < 5; i++) { - inspectorRule.setupNewSubscriberAndComplete(i+1, false); - } - - inspectorRule.channel.runPendingTasks(); - - inspectorRule.sendMessages(sub1, 35); - assertThat("Unexpected items requested.", producer1.getRequested(), is(95L)); - } - - @Test(timeout = 10000) - public void testMinRequestN() throws Exception { - for (int i=1; i < 66; i++) { - inspectorRule.setupNewSubscriberAndComplete(i, false); - } - WriteStreamSubscriber sub1 = inspectorRule.newSubscriber(); - MockProducer producer1 = inspectorRule.setupSubscriberAndValidate(sub1, 66); - assertThat("Unexpected items requested from producer.", producer1.getRequested(), is(1L)); - - inspectorRule.channel.runPendingTasks(); - inspectorRule.sendMessages(sub1, 35); - assertThat("Unexpected items requested.", producer1.getRequested(), greaterThan(1L)); - } - - public static class InspectorRule extends ExternalResource { - - private BytesWriteInterceptor interceptor; - private EmbeddedChannel channel; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - interceptor = new BytesWriteInterceptor("foo"); - channel = new TestEmbeddedChannel(new WriteTransformer(), interceptor); - base.evaluate(); - } - }; - } - - WriteStreamSubscriber newSubscriber() { - return interceptor.newSubscriber(channel.pipeline().lastContext(), channel.newPromise()); - } - - private MockProducer setupSubscriberAndValidate(WriteStreamSubscriber sub, int expectedSubCount) { - MockProducer mockProducer = setupSubscriber(sub); - assertThat("Subscriber not added.", interceptor.getSubscribers(), hasSize(expectedSubCount)); - assertThat("Subscriber not added.", interceptor.getSubscribers().get(expectedSubCount - 1), equalTo(sub)); - return mockProducer; - } - - private static MockProducer setupSubscriber(WriteStreamSubscriber sub) { - sub.onStart(); - MockProducer mockProducer = new MockProducer(); - sub.setProducer(mockProducer); - return mockProducer; - } - - public static Long defaultRequestN() { - return Long.valueOf(MAX_PER_SUBSCRIBER_REQUEST); - } - - public void sendMessages(WriteStreamSubscriber subscriber, int msgCount) { - for(int i=0; i < msgCount; i++) { - subscriber.onNext("Hello"); - channel.write("Hello"); - } - channel.flush(); - } - - public void sendMessages(int msgCount) { - for(int i=0; i < msgCount; i++) { - channel.write("Hello"); - } - channel.flush(); - } - - public void setupNewSubscriberAndComplete(int expectedSubCount, boolean runPendingTasks) { - WriteStreamSubscriber sub2 = newSubscriber(); - MockProducer producer2 = setupSubscriberAndValidate(sub2, expectedSubCount); - assertThat("Unexpected items requested from producer.", producer2.getRequested(), - lessThanOrEqualTo(Math.max(1, defaultRequestN()/expectedSubCount))); - sub2.onCompleted(); - sub2.unsubscribe(); - if (runPendingTasks) { - channel.runPendingTasks(); - } - } - - public Queue getWrittenMessages() { - channel.runPendingTasks(); - channel.flush(); - return channel.outboundMessages(); - } - - public void setEventLoopThread() { - ChannelPromise deregisterPromise = channel.newPromise(); - channel.deregister(deregisterPromise); - channel.runPendingTasks(); - assertThat("failed to deregister", deregisterPromise.isDone() && deregisterPromise.isSuccess()); - - ThreadAwareEmbeddedEventLoop loop = new ThreadAwareEmbeddedEventLoop(Thread.currentThread()); - ChannelFuture registerPromise = loop.register(channel); - assertThat("failed to register", registerPromise.isDone() && registerPromise.isSuccess()); - } - - private void sendFromOtherThread(final WriteStreamSubscriber subscriber, Scheduler.Worker worker, final Object msg) throws InterruptedException { - final CountDownLatch countDown = new CountDownLatch(1); - worker.schedule(new Action0() { - @Override - public void call() { - subscriber.onNext(msg); - countDown.countDown(); - } - }); - countDown.await(); - } - } - - /** - * A custom EmbeddedChannel allowing a special EventLoop, so that we can simulate calls not coming from the event loop. - */ - private static class TestEmbeddedChannel extends EmbeddedChannel { - - public TestEmbeddedChannel(WriteTransformer writeTransformer, BytesWriteInterceptor interceptor) { - super(writeTransformer, interceptor); - } - - @Override - protected boolean isCompatible(EventLoop loop) { - return loop instanceof ThreadAwareEmbeddedEventLoop || super.isCompatible(loop); - } - - @Override - public void runPendingTasks() { - if (super.eventLoop() instanceof ThreadAwareEmbeddedEventLoop) { - ThreadAwareEmbeddedEventLoop loop = (ThreadAwareEmbeddedEventLoop) super.eventLoop(); - loop.runTasks(); - } else { - super.runPendingTasks(); - } - } - } - - /** - * Need an embedded event loop that considers a single thread to be "on the loop" in order to have writes from - * outside the event loop. - * Due to final modifier of EmbeddedEventLoop there was some copying needed. - */ - private static class ThreadAwareEmbeddedEventLoop extends AbstractScheduledEventExecutor implements EventLoop { - - private final Queue tasks = new ArrayDeque(2); - private final Thread loopThread; - - public ThreadAwareEmbeddedEventLoop(Thread loopThread) { - this.loopThread = loopThread; - } - - @Override - public EventLoopGroup parent() { - return (EventLoopGroup) super.parent(); - } - - @Override - public EventLoop next() { - return (EventLoop) super.next(); - } - - @Override - public void execute(Runnable command) { - if (command == null) { - throw new NullPointerException("command"); - } - tasks.add(command); - } - - void runTasks() { - for (;;) { - Runnable task = tasks.poll(); - if (task == null) { - break; - } - - task.run(); - } - } - - @Override - protected void cancelScheduledTasks() { - super.cancelScheduledTasks(); - } - - @Override - public Future shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) { - throw new UnsupportedOperationException(); - } - - @Override - public Future terminationFuture() { - throw new UnsupportedOperationException(); - } - - @Override - @Deprecated - public void shutdown() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isShuttingDown() { - return false; - } - - @Override - public boolean isShutdown() { - return false; - } - - @Override - public boolean isTerminated() { - return false; - } - - @Override - public boolean awaitTermination(long timeout, TimeUnit unit) { - return false; - } - - @Override - public ChannelFuture register(Channel channel) { - return register(new DefaultChannelPromise(channel, this)); - } - - @Override - public ChannelFuture register(ChannelPromise promise) { - ObjectUtil.checkNotNull(promise, "promise"); - promise.channel().unsafe().register(this, promise); - return promise; - } - - @Deprecated - @Override - public ChannelFuture register(Channel channel, ChannelPromise promise) { - channel.unsafe().register(this, promise); - return promise; - } - - @Override - public boolean inEventLoop() { - return Thread.currentThread() == loopThread; - } - - @Override - public boolean inEventLoop(Thread thread) { - return thread == loopThread; - } - } - -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/channel/ConnectionImplTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/channel/ConnectionImplTest.java deleted file mode 100644 index 1a48e72..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/channel/ConnectionImplTest.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.FileRegion; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.logging.LoggingHandler; -import io.reactivex.netty.test.util.FlushSelector; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.mockito.Mockito; -import rx.Observable; - -public class ConnectionImplTest { - - @Rule - public final ConnRule connRule = new ConnRule(); - - @Test(timeout = 60000) - public void testWrite() throws Exception { - - Observable toWrite = Observable.empty(); - connRule.connection.write(toWrite); - - Mockito.verify(connRule.channelOperations).write(toWrite); - Mockito.verifyNoMoreInteractions(connRule.channelOperations); - } - - @Test(timeout = 60000) - public void testWriteWithFlushSelector() throws Exception { - Observable toWrite = Observable.empty(); - FlushSelector flushSelector = new FlushSelector<>(1); - connRule.connection.write(toWrite, flushSelector); - - Mockito.verify(connRule.channelOperations).write(toWrite, flushSelector); - Mockito.verifyNoMoreInteractions(connRule.channelOperations); - } - - @Test(timeout = 60000) - public void testWriteAndFlushOnEach() throws Exception { - Observable toWrite = Observable.empty(); - connRule.connection.writeAndFlushOnEach(toWrite); - - Mockito.verify(connRule.channelOperations).writeAndFlushOnEach(toWrite); - Mockito.verifyNoMoreInteractions(connRule.channelOperations); - } - - @Test(timeout = 60000) - public void testWriteString() throws Exception { - Observable toWrite = Observable.empty(); - connRule.connection.writeString(toWrite); - - Mockito.verify(connRule.channelOperations).writeString(toWrite); - Mockito.verifyNoMoreInteractions(connRule.channelOperations); - - } - - @Test(timeout = 60000) - public void testWriteStringWithFlushSelector() throws Exception { - Observable toWrite = Observable.empty(); - FlushSelector flushSelector = new FlushSelector<>(1); - connRule.connection.writeString(toWrite, flushSelector); - - Mockito.verify(connRule.channelOperations).writeString(toWrite, flushSelector); - Mockito.verifyNoMoreInteractions(connRule.channelOperations); - } - - @Test(timeout = 60000) - public void testWriteStringAndFlushOnEach() throws Exception { - Observable toWrite = Observable.empty(); - connRule.connection.writeStringAndFlushOnEach(toWrite); - - Mockito.verify(connRule.channelOperations).writeStringAndFlushOnEach(toWrite); - Mockito.verifyNoMoreInteractions(connRule.channelOperations); - } - - @Test(timeout = 60000) - public void testWriteBytes() throws Exception { - Observable toWrite = Observable.empty(); - connRule.connection.writeBytes(toWrite); - - Mockito.verify(connRule.channelOperations).writeBytes(toWrite); - Mockito.verifyNoMoreInteractions(connRule.channelOperations); - - } - - @Test(timeout = 60000) - public void testWriteBytesWithFlushSelector() throws Exception { - Observable toWrite = Observable.empty(); - FlushSelector flushSelector = new FlushSelector<>(1); - connRule.connection.writeBytes(toWrite, flushSelector); - - Mockito.verify(connRule.channelOperations).writeBytes(toWrite, flushSelector); - Mockito.verifyNoMoreInteractions(connRule.channelOperations); - } - - @Test(timeout = 60000) - public void testWriteBytesAndFlushOnEach() throws Exception { - Observable toWrite = Observable.empty(); - connRule.connection.writeBytesAndFlushOnEach(toWrite); - - Mockito.verify(connRule.channelOperations).writeBytesAndFlushOnEach(toWrite); - Mockito.verifyNoMoreInteractions(connRule.channelOperations); - } - - @Test(timeout = 60000) - public void testWriteFileRegion() throws Exception { - Observable toWrite = Observable.empty(); - connRule.connection.writeFileRegion(toWrite); - - Mockito.verify(connRule.channelOperations).writeFileRegion(toWrite); - Mockito.verifyNoMoreInteractions(connRule.channelOperations); - - } - - @Test(timeout = 60000) - public void testWriteFileRegionWithFlushSelector() throws Exception { - Observable toWrite = Observable.empty(); - FlushSelector flushSelector = new FlushSelector<>(1); - connRule.connection.writeFileRegion(toWrite, flushSelector); - - Mockito.verify(connRule.channelOperations).writeFileRegion(toWrite, flushSelector); - Mockito.verifyNoMoreInteractions(connRule.channelOperations); - } - - @Test(timeout = 60000) - public void testWriteFileRegionAndFlushOnEach() throws Exception { - Observable toWrite = Observable.empty(); - connRule.connection.writeFileRegionAndFlushOnEach(toWrite); - - Mockito.verify(connRule.channelOperations).writeFileRegionAndFlushOnEach(toWrite); - Mockito.verifyNoMoreInteractions(connRule.channelOperations); - } - - @Test(timeout = 60000) - public void testFlush() throws Exception { - connRule.connection.flush(); - - Mockito.verify(connRule.channelOperations).flush(); - Mockito.verifyNoMoreInteractions(connRule.channelOperations); - } - - @Test(timeout = 60000) - public void testClose() throws Exception { - connRule.connection.close(); - - Mockito.verify(connRule.channelOperations).close(); - Mockito.verifyNoMoreInteractions(connRule.channelOperations); - } - - @Test(timeout = 60000) - public void testCloseWithoutFlush() throws Exception { - connRule.connection.close(false); - - Mockito.verify(connRule.channelOperations).close(false); - Mockito.verifyNoMoreInteractions(connRule.channelOperations); - } - - public static class ConnRule extends ExternalResource { - - private ChannelOperations channelOperations; - private Connection connection; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - final EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - - @SuppressWarnings("unchecked") - ChannelOperations channelOperations = Mockito.mock(ChannelOperations.class); - - ConnRule.this.channelOperations = channelOperations; - connection = ConnectionImpl.create(channel, ConnRule.this.channelOperations); - base.evaluate(); - } - }; - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/channel/ContentSourceRule.java b/netty-http-rx/src/test/java/io/reactivex/netty/channel/ContentSourceRule.java deleted file mode 100644 index 3f47969..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/channel/ContentSourceRule.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.Observable; -import rx.Subscriber; -import rx.functions.Func1; -import rx.observers.TestSubscriber; - -import static org.hamcrest.core.Is.*; -import static org.junit.Assert.*; - -public class ContentSourceRule extends ExternalResource implements Func1, Object> { - - private ByteBuf data; - private EmbeddedChannel channel; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - data = Unpooled.buffer().writeBytes("Hello".getBytes()); - channel = new EmbeddedChannel(new ChannelDuplexHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof SourceEvent) { - SourceEvent sourceEvent = (SourceEvent) evt; - sourceEvent.subscriber.onNext(data); - sourceEvent.subscriber.onCompleted(); - } - super.userEventTriggered(ctx, evt); - } - }); - base.evaluate(); - } - }; - } - - public EmbeddedChannel getChannel() { - return channel; - } - - public ByteBuf getData() { - return data; - } - - public TestSubscriber subscribe(Observable source, int expectedRefCnt) { - TestSubscriber subscriber = new TestSubscriber<>(); - source.subscribe(subscriber); - subscriber.awaitTerminalEvent(); - subscriber.assertValue(data); - - ByteBuf data = subscriber.getOnNextEvents().get(0); - - assertThat("Unexpected ref count of data", data.refCnt(), is(expectedRefCnt)); - return subscriber; - } - - @Override - public Object call(Subscriber subscriber) { - return new SourceEvent(subscriber); - } - - public static class SourceEvent { - - private final Subscriber subscriber; - - public SourceEvent(Subscriber subscriber) { - this.subscriber = subscriber; - } - - public Subscriber getSubscriber() { - return subscriber; - } - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/channel/ContentSourceTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/channel/ContentSourceTest.java deleted file mode 100644 index 68c1c1e..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/channel/ContentSourceTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.buffer.ByteBuf; -import org.junit.Rule; -import org.junit.Test; -import rx.observers.TestSubscriber; - -import static org.hamcrest.core.Is.*; -import static org.junit.Assert.*; - -public class ContentSourceTest { - - @Rule - public final ContentSourceRule sourceRule = new ContentSourceRule(); - - @Test(timeout = 60000) - public void testNoAutoRelease() throws Exception { - ContentSource source = new ContentSource<>(sourceRule.getChannel(), sourceRule); - sourceRule.subscribe(source, 1); - } - - @Test(timeout = 60000) - public void testAutoRelease() throws Exception { - ContentSource source = new ContentSource<>(sourceRule.getChannel(), sourceRule); - sourceRule.subscribe(source.autoRelease(), 0); - } - - @Test(timeout = 60000) - public void testReplayable() throws Exception { - DisposableContentSource disposable = new ContentSource<>(sourceRule.getChannel(), sourceRule).replayable(); - sourceRule.subscribe(disposable.autoRelease(), 1); - sourceRule.subscribe(disposable.autoRelease(), 1); - - assertThat("Unexpected ref count before dispose.", sourceRule.getData().refCnt(), is(1)); - - disposable.dispose(); - - assertThat("Unexpected ref count after dispose.", sourceRule.getData().refCnt(), is(0)); - } - - @Test(timeout = 60000) - public void testSubscribePostDispose() throws Exception { - DisposableContentSource disposable = new ContentSource<>(sourceRule.getChannel(), sourceRule).replayable(); - sourceRule.subscribe(disposable.autoRelease(), 1); - disposable.dispose(); - - TestSubscriber subscriber = new TestSubscriber<>(); - disposable.subscribe(subscriber); - subscriber.awaitTerminalEvent(); - subscriber.assertError(IllegalStateException.class); - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/channel/DefaultChannelOperationsTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/channel/DefaultChannelOperationsTest.java deleted file mode 100644 index d72883e..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/channel/DefaultChannelOperationsTest.java +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.channel.DefaultFileRegion; -import io.netty.channel.FileRegion; -import io.netty.channel.embedded.EmbeddedChannel; -import io.reactivex.netty.test.util.FlushSelector; -import io.reactivex.netty.test.util.MockEventPublisher; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.Observable; -import rx.functions.Action1; -import rx.observers.TestSubscriber; - -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; - -import static io.reactivex.netty.test.util.MockEventPublisher.*; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class DefaultChannelOperationsTest { - - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - - @Rule - public final ChannelOpRule channelOpRule = new ChannelOpRule(); - - @Test(timeout = 60000) - public void testWrite() throws Exception { - final String msg = "Hello"; - Observable writeO = channelOpRule.channelOperations.write(ChannelOpRule.bbJust(msg)); - - _testWrite(writeO, msg); - } - - @Test(timeout = 60000) - public void testWriteWithFlushSelector() throws Exception { - final String msg1 = "Hello1"; - final String msg2 = "Hello2"; - - Observable writeO = channelOpRule.channelOperations.write(ChannelOpRule.bbJust(msg1, msg2), - new FlushSelector(1)); - _testWithFlushSelector(writeO, msg1, msg2); - } - - @Test(timeout = 60000) - public void testWriteAndFlushOnEach() throws Exception { - final String msg1 = "Hello1"; - final String msg2 = "Hello2"; - - Observable writeO = channelOpRule.channelOperations.writeAndFlushOnEach(ChannelOpRule.bbJust(msg1, msg2)); - _testWithFlushSelector(writeO, msg1, msg2); - } - - @Test(timeout = 60000) - public void testWriteString() throws Exception { - final String msg = "Hello"; - Observable writeO = channelOpRule.channelOperations.writeString(Observable.just(msg)); - - _testWrite(writeO, msg); - } - - @Test(timeout = 60000) - public void testWriteStringWithFlushSelector() throws Exception { - final String msg1 = "Hello1"; - final String msg2 = "Hello2"; - - Observable writeO = channelOpRule.channelOperations.writeString(Observable.just(msg1, msg2), - new FlushSelector(1)); - _testWithFlushSelector(writeO, msg1, msg2); - } - - @Test(timeout = 60000) - public void testWriteStringAndFlushOnEach() throws Exception { - final String msg1 = "Hello1"; - final String msg2 = "Hello2"; - - Observable writeO = channelOpRule.channelOperations.writeStringAndFlushOnEach(Observable.just(msg1, msg2)); - _testWithFlushSelector(writeO, msg1, msg2); - } - - @Test(timeout = 60000) - public void testWriteBytes() throws Exception { - final String msg = "Hello"; - Observable writeO = channelOpRule.channelOperations.writeBytes(Observable.just(msg.getBytes())); - - _testWrite(writeO, msg); - } - - @Test(timeout = 60000) - public void testWriteBytesWithFlushSelector() throws Exception { - final String msg1 = "Hello1"; - final String msg2 = "Hello2"; - - Observable writeO = channelOpRule.channelOperations.writeBytes(Observable.just(msg1.getBytes(), - msg2.getBytes()), - new FlushSelector(1)); - _testWithFlushSelector(writeO, msg1, msg2); - } - - @Test(timeout = 60000) - public void testWriteBytesAndFlushOnEach() throws Exception { - final String msg1 = "Hello1"; - final String msg2 = "Hello2"; - - Observable writeO = channelOpRule.channelOperations.writeBytesAndFlushOnEach( - Observable.just(msg1.getBytes(), - msg2.getBytes())); - _testWithFlushSelector(writeO, msg1, msg2); - } - - @Test(timeout = 60000) - public void testWriteFileRegion() throws Exception { - FileRegion msg = new DefaultFileRegion(folder.newFile("msg.txt"), 0, 0); - - Observable writeO = channelOpRule.channelOperations.writeFileRegion(Observable.just(msg)); - - _testWrite(writeO, msg); - } - - @Test(timeout = 60000) - public void testWriteFileRegionWithFlushSelector() throws Exception { - FileRegion msg1 = new DefaultFileRegion(folder.newFile("msg1.txt"), 0, 0); - FileRegion msg2 = new DefaultFileRegion(folder.newFile("msg2.txt"), 0, 0); - - Observable writeO = channelOpRule.channelOperations.writeFileRegion(Observable.just(msg1, msg2), - new FlushSelector(1)); - - _testWithFlushSelector(writeO, msg1, msg2); - - } - - @Test(timeout = 60000) - public void testWriteFileRegionAndFlushOnEach() throws Exception { - FileRegion msg1 = new DefaultFileRegion(folder.newFile("msg1.txt"), 0, 0); - FileRegion msg2 = new DefaultFileRegion(folder.newFile("msg2.txt"), 0, 0); - - Observable writeO = channelOpRule.channelOperations - .writeFileRegionAndFlushOnEach(Observable.just(msg1, msg2)); - - _testWithFlushSelector(writeO, msg1, msg2); - } - - @Test(timeout = 60000) - public void testFlush() throws Exception { - String msg = "Hello"; - channelOpRule.channel.write(Unpooled.buffer().writeBytes(msg.getBytes())); - - channelOpRule.channelOperations.flush(); - - channelOpRule.verifyOutboundMessages(msg); - } - - @Test(timeout = 60000) - public void testCloseWithFlush() throws Exception { - TestSubscriber subscriber = new TestSubscriber<>(); - channelOpRule.channelOperations.close().subscribe(subscriber); - - subscriber.assertTerminalEvent(); - subscriber.assertNoErrors(); - - assertThat("Channel not closed.", channelOpRule.channel.isOpen(), is(false)); - } - - @Test(timeout = 60000) - public void testCloseWithoutFlush() throws Exception { - TestSubscriber subscriber = new TestSubscriber<>(); - channelOpRule.channel.write("Hello"); - - channelOpRule.channelOperations.close(false).subscribe(subscriber); - - subscriber.assertTerminalEvent(); - subscriber.assertNoErrors(); - - channelOpRule.verifyOutboundMessages(); - assertThat("Channel not closed.", channelOpRule.channel.isOpen(), is(false)); - } - - private void _testWithFlushSelector(Observable writeObservable, Object expected1, Object expected2) { - final TestSubscriber writeSub = new TestSubscriber<>(); - - writeObservable.subscribe(writeSub); - - assertThat("Unexpected write subscribers on the channel.", channelOpRule.writeObservableSubscribers, - hasSize(1)); - - ChannelOpRule.TestWriteSubscriber testSubscriber = channelOpRule.writeObservableSubscribers.remove(0); - - channelOpRule.verifyOutboundMessages(expected1); - channelOpRule.channel.outboundMessages().clear(); - - testSubscriber.requestMore(1); - channelOpRule.verifyOutboundMessages(expected2); - - testSubscriber.awaitTerminalEvent(); - - testSubscriber.finishOverarchingWritePromiseIfAllPromisesFinished(); - - writeSub.assertNoErrors(); - writeSub.assertTerminalEvent(); - } - - private void _testWrite(Observable writeObservable, Object expected) { - final TestSubscriber writeSub = new TestSubscriber<>(); - - writeObservable.subscribe(writeSub); - - assertThat("Unexpected write subscribers on the channel.", channelOpRule.writeObservableSubscribers, - hasSize(1)); - - ChannelOpRule.TestWriteSubscriber testSubscriber = channelOpRule.writeObservableSubscribers.remove(0); - testSubscriber.finishOverarchingWritePromiseIfAllPromisesFinished(); - testSubscriber.awaitTerminalEvent(); - - writeSub.assertNoErrors(); - writeSub.assertTerminalEvent(); - - channelOpRule.verifyOutboundMessages(expected); - } - - @Test(timeout = 60000) - public void testCloseListener() throws Exception { - Observable closeListener = channelOpRule.channelOperations.closeListener(); - TestSubscriber subscriber = new TestSubscriber<>(); - closeListener.subscribe(subscriber); - - subscriber.assertNoTerminalEvent(); - - subscriber.unsubscribe(); - - subscriber.assertNoTerminalEvent(); - - channelOpRule.channel.close().sync(); - - subscriber.assertNoTerminalEvent(); - } - - public static class ChannelOpRule extends ExternalResource { - - private DefaultChannelOperations channelOperations; - private EmbeddedChannel channel; - private List writeObservableSubscribers; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - writeObservableSubscribers = new ArrayList<>(); - /*Since, the appropriate handler is not added to the pipeline that handles O<> writes.*/ - channel = new EmbeddedChannel(new HandleObservableWrite(writeObservableSubscribers)); - channelOperations = new DefaultChannelOperations<>(channel, null, disabled()); - base.evaluate(); - } - }; - } - - public static Observable bbJust(String... items) { - List bbItems = new ArrayList<>(); - - for (String item : items) { - bbItems.add(Unpooled.buffer().writeBytes(item.getBytes())); - } - - return Observable.from(bbItems); - } - - public void verifyOutboundMessages(Object... msgs) { - - boolean stringConversionRequired = msgs != null && msgs.length != 0 && msgs[0] instanceof String; - - final List outMsgsToTest = new ArrayList<>(); - - for (Object next : channel.outboundMessages()) { - if (stringConversionRequired) { - if (next instanceof ByteBuf) { - outMsgsToTest.add(((ByteBuf) next).toString(Charset.defaultCharset())); - } - } else { - outMsgsToTest.add(next); - } - } - - if (null == msgs || msgs.length == 0) { - assertThat("Unexpected messages written on the channel.", outMsgsToTest, is(empty())); - } else { - assertThat("Unexpected messages written on the channel.", outMsgsToTest, contains(msgs)); - } - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private static class HandleObservableWrite extends ChannelDuplexHandler { - - private final List writeObservableSubscribers; - - public HandleObservableWrite(List writeObservableSubscribers) { - this.writeObservableSubscribers = writeObservableSubscribers; - } - - @Override - public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) - throws Exception { - if (msg instanceof Observable) { - Observable msgO = (Observable) msg; - final TestWriteSubscriber testSubscriber = new TestWriteSubscriber(promise); - msgO.doOnNext(new Action1() { - @Override - public void call(Object o) { - final ChannelPromise channelPromise = ctx.newPromise(); - testSubscriber.allPromises.add(channelPromise); - - if (o instanceof String) { - o = Unpooled.buffer().writeBytes(((String) o).getBytes()); - } else if (o instanceof byte[]) { - o = Unpooled.buffer().writeBytes((byte[]) o); - } - ctx.write(o, channelPromise); - } - }).doOnError(new Action1() { - @Override - public void call(Throwable throwable) { - ctx.fireExceptionCaught(throwable); - } - }).subscribe(testSubscriber); - - writeObservableSubscribers.add(testSubscriber); - } else { - super.write(ctx, msg, promise); - } - } - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private static class TestWriteSubscriber extends TestSubscriber { - - private final List allPromises = new ArrayList<>(); - private final ChannelPromise overarchingPromise; - - public TestWriteSubscriber(ChannelPromise promise) { - overarchingPromise = promise; - } - - public void finishOverarchingWritePromiseIfAllPromisesFinished() { - for (ChannelPromise aPromise : allPromises) { - if (aPromise.isDone()) { - if (!aPromise.isSuccess()) { - overarchingPromise.tryFailure(aPromise.cause()); - return; - } - } else { - overarchingPromise.tryFailure(new IllegalStateException("A write promise did not complete.")); - return; - } - } - - overarchingPromise.trySuccess(); - } - - @Override - public void onStart() { - request(1); - } - } - } - -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/channel/DetachedChannelPipelineTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/channel/DetachedChannelPipelineTest.java deleted file mode 100644 index fda3d1b..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/channel/DetachedChannelPipelineTest.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.reactivex.netty.channel.DetachedChannelPipeline.HandlerHolder; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.functions.Action1; -import rx.functions.Func0; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class DetachedChannelPipelineTest { - - private static final EventLoopGroup MULTI_GRP = new NioEventLoopGroup(); - - public static final Func0 HANDLER_FACTORY = new Func0() { - @Override - public ChannelHandler call() { - return new ChannelDuplexHandler(); - } - }; - private static final HandlerHolder HANDLER_1_NO_NAME = new HandlerHolder(HANDLER_FACTORY); - private static final HandlerHolder HANDLER_1 = new HandlerHolder("handler-1", HANDLER_FACTORY); - private static final HandlerHolder HANDLER_1_GRP = - new HandlerHolder("handler-1", HANDLER_FACTORY, new NioEventLoopGroup()); - private static final HandlerHolder HANDLER_1_GRP_NO_NAME = - new HandlerHolder(null, HANDLER_FACTORY, MULTI_GRP); - - private static final HandlerHolder HANDLER_2_NO_NAME = new HandlerHolder(HANDLER_FACTORY); - private static final HandlerHolder HANDLER_2 = new HandlerHolder("handler-2", HANDLER_FACTORY); - private static final HandlerHolder HANDLER_2_GRP = - new HandlerHolder("handler-2", HANDLER_FACTORY, new NioEventLoopGroup()); - private static final HandlerHolder HANDLER_2_GRP_NO_NAME = - new HandlerHolder(null, HANDLER_FACTORY, MULTI_GRP); - - @Rule - public final PipelineRule pipelineRule = new PipelineRule(); - - @Test(timeout = 60000) - public void testCopy() throws Exception { - pipelineRule.pipeline.addLast(HANDLER_1.getNameIfConfigured(), HANDLER_1.getHandlerFactoryIfConfigured()); - pipelineRule.pipeline.addLast(HANDLER_2.getNameIfConfigured(), HANDLER_2.getHandlerFactoryIfConfigured()); - - DetachedChannelPipeline copy = pipelineRule.pipeline.copy(); - assertThat("Copy did not create a new instance.", copy, not(pipelineRule.pipeline)); - - assertThat("Unexpected handlers count in the copy.", copy.getHoldersInOrder(), hasSize(2)); - assertThat("Unexpected handlers in the copy.", copy.getHoldersInOrder(), - contains(pipelineRule.pipeline.getHoldersInOrder().toArray())); - } - - @Test(timeout = 60000) - public void testAddFirst() throws Exception { - pipelineRule.pipeline.addFirst(HANDLER_1.getNameIfConfigured(), HANDLER_1.getHandlerFactoryIfConfigured()); - pipelineRule.pipeline.addFirst(HANDLER_2.getNameIfConfigured(), HANDLER_2.getHandlerFactoryIfConfigured()); - - assertThat("Unexpected handlers count.", pipelineRule.pipeline.getHoldersInOrder(), hasSize(2)); - assertThat("Unexpected handlers.", pipelineRule.pipeline.getHoldersInOrder(), - contains(HANDLER_2, HANDLER_1)); - } - - @Test(timeout = 60000) - public void testAddFirstWithGroup() throws Exception { - pipelineRule.pipeline.addFirst(HANDLER_1_GRP.getGroupIfConfigured(), HANDLER_1_GRP.getNameIfConfigured(), - HANDLER_1_GRP.getHandlerFactoryIfConfigured()); - pipelineRule.pipeline.addFirst(HANDLER_2_GRP.getGroupIfConfigured(), HANDLER_2_GRP.getNameIfConfigured(), - HANDLER_2_GRP.getHandlerFactoryIfConfigured()); - - assertThat("Unexpected handlers count.", pipelineRule.pipeline.getHoldersInOrder(), hasSize(2)); - assertThat("Unexpected handlers.", pipelineRule.pipeline.getHoldersInOrder(), - contains(HANDLER_2_GRP, HANDLER_1_GRP)); - } - - @Test(timeout = 60000) - public void testAddLast() throws Exception { - pipelineRule.pipeline.addLast(HANDLER_1.getNameIfConfigured(), HANDLER_1.getHandlerFactoryIfConfigured()); - pipelineRule.pipeline.addLast(HANDLER_2.getNameIfConfigured(), HANDLER_2.getHandlerFactoryIfConfigured()); - - assertThat("Unexpected handlers count.", pipelineRule.pipeline.getHoldersInOrder(), hasSize(2)); - assertThat("Unexpected handlers.", pipelineRule.pipeline.getHoldersInOrder(), - contains(HANDLER_1, HANDLER_2)); - } - - @Test(timeout = 60000) - public void testAddLastWithGroup() throws Exception { - pipelineRule.pipeline.addLast(HANDLER_1_GRP.getGroupIfConfigured(), HANDLER_1_GRP.getNameIfConfigured(), - HANDLER_1_GRP.getHandlerFactoryIfConfigured()); - pipelineRule.pipeline.addLast(HANDLER_2_GRP.getGroupIfConfigured(), HANDLER_2_GRP.getNameIfConfigured(), - HANDLER_2_GRP.getHandlerFactoryIfConfigured()); - - assertThat("Unexpected handlers count.", pipelineRule.pipeline.getHoldersInOrder(), hasSize(2)); - assertThat("Unexpected handlers.", pipelineRule.pipeline.getHoldersInOrder(), - contains(HANDLER_1_GRP, HANDLER_2_GRP)); - } - - @Test(timeout = 60000) - public void testAddBefore() throws Exception { - pipelineRule.pipeline.addLast(HANDLER_1.getGroupIfConfigured(), - HANDLER_1.getNameIfConfigured(), HANDLER_1.getHandlerFactoryIfConfigured()); - pipelineRule.pipeline.addBefore(HANDLER_1.getNameIfConfigured(), - HANDLER_2.getNameIfConfigured(), - HANDLER_2.getHandlerFactoryIfConfigured()); - - assertThat("Unexpected handlers count.", pipelineRule.pipeline.getHoldersInOrder(), hasSize(2)); - assertThat("Unexpected handlers.", pipelineRule.pipeline.getHoldersInOrder(), - contains(HANDLER_2, HANDLER_1)); - } - - @Test(timeout = 60000) - public void testAddBeforeWithGroup() throws Exception { - pipelineRule.pipeline.addLast(HANDLER_1_GRP.getGroupIfConfigured(), - HANDLER_1_GRP.getNameIfConfigured(), HANDLER_1_GRP.getHandlerFactoryIfConfigured()); - pipelineRule.pipeline.addBefore(HANDLER_2_GRP.getGroupIfConfigured(), - HANDLER_1_GRP.getNameIfConfigured(), - HANDLER_2_GRP.getNameIfConfigured(), - HANDLER_2_GRP.getHandlerFactoryIfConfigured()); - - assertThat("Unexpected handlers count.", pipelineRule.pipeline.getHoldersInOrder(), hasSize(2)); - assertThat("Unexpected handlers.", pipelineRule.pipeline.getHoldersInOrder(), - contains(HANDLER_2_GRP, HANDLER_1_GRP)); - } - - @Test(timeout = 60000) - public void testAddAfter() throws Exception { - pipelineRule.pipeline.addLast(HANDLER_1.getGroupIfConfigured(), - HANDLER_1.getNameIfConfigured(), HANDLER_1.getHandlerFactoryIfConfigured()); - pipelineRule.pipeline.addAfter(HANDLER_1.getNameIfConfigured(), - HANDLER_2.getNameIfConfigured(), - HANDLER_2.getHandlerFactoryIfConfigured()); - - assertThat("Unexpected handlers count.", pipelineRule.pipeline.getHoldersInOrder(), hasSize(2)); - assertThat("Unexpected handlers.", pipelineRule.pipeline.getHoldersInOrder(), - contains(HANDLER_1, HANDLER_2)); - } - - @Test(timeout = 60000) - public void testAddAfterWithGroup() throws Exception { - pipelineRule.pipeline.addLast(HANDLER_1_GRP.getGroupIfConfigured(), - HANDLER_1_GRP.getNameIfConfigured(), HANDLER_1_GRP.getHandlerFactoryIfConfigured()); - pipelineRule.pipeline.addAfter(HANDLER_2_GRP.getGroupIfConfigured(), HANDLER_1_GRP.getNameIfConfigured(), - HANDLER_2_GRP.getNameIfConfigured(), - HANDLER_2_GRP.getHandlerFactoryIfConfigured()); - - assertThat("Unexpected handlers count.", pipelineRule.pipeline.getHoldersInOrder(), hasSize(2)); - assertThat("Unexpected handlers.", pipelineRule.pipeline.getHoldersInOrder(), - contains(HANDLER_1_GRP, HANDLER_2_GRP)); - } - - @Test(timeout = 60000) - public void testAddFirstMulti() throws Exception { - pipelineRule.pipeline.addFirst(HANDLER_1_NO_NAME.getHandlerFactoryIfConfigured(), HANDLER_2_NO_NAME.getHandlerFactoryIfConfigured()); - - assertThat("Unexpected handlers count.", pipelineRule.pipeline.getHoldersInOrder(), hasSize(2)); - assertThat("Unexpected handlers.", pipelineRule.pipeline.getHoldersInOrder(), - contains(HANDLER_1_NO_NAME, HANDLER_2_NO_NAME)); - } - - @Test(timeout = 60000) - public void testAddFirstMultiWithGroup() throws Exception { - pipelineRule.pipeline.addFirst(MULTI_GRP, HANDLER_1_GRP_NO_NAME.getHandlerFactoryIfConfigured(), - HANDLER_2_GRP_NO_NAME.getHandlerFactoryIfConfigured()); - - assertThat("Unexpected handlers count.", pipelineRule.pipeline.getHoldersInOrder(), hasSize(2)); - assertThat("Unexpected handlers.", pipelineRule.pipeline.getHoldersInOrder(), - contains(HANDLER_1_GRP_NO_NAME, HANDLER_2_GRP_NO_NAME)); - } - - @Test(timeout = 60000) - public void testAddLastMulti() throws Exception { - pipelineRule.pipeline.addLast(HANDLER_1_NO_NAME.getHandlerFactoryIfConfigured(), - HANDLER_2_NO_NAME.getHandlerFactoryIfConfigured()); - - assertThat("Unexpected handlers count.", pipelineRule.pipeline.getHoldersInOrder(), hasSize(2)); - assertThat("Unexpected handlers.", pipelineRule.pipeline.getHoldersInOrder(), - contains(HANDLER_1_NO_NAME, HANDLER_2_NO_NAME)); - } - - @Test(timeout = 60000) - public void testAddLastMultiWithGroup() throws Exception { - pipelineRule.pipeline.addLast(MULTI_GRP, HANDLER_1_GRP_NO_NAME.getHandlerFactoryIfConfigured(), - HANDLER_2_GRP_NO_NAME.getHandlerFactoryIfConfigured()); - - assertThat("Unexpected handlers count.", pipelineRule.pipeline.getHoldersInOrder(), hasSize(2)); - assertThat("Unexpected handlers.", pipelineRule.pipeline.getHoldersInOrder(), - contains(HANDLER_1_GRP_NO_NAME, HANDLER_2_GRP_NO_NAME)); - } - - public static class PipelineRule extends ExternalResource { - - private DetachedChannelPipeline pipeline; - private ChannelDuplexHandler tail; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - tail = new ChannelDuplexHandler(); - pipeline = new DetachedChannelPipeline(new Action1() { - @Override - public void call(ChannelPipeline pipeline1) { - pipeline1.addLast(tail); - } - }); - base.evaluate(); - } - }; - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/channel/ReadProducerTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/channel/ReadProducerTest.java deleted file mode 100644 index eb6fe55..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/channel/ReadProducerTest.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.logging.LoggingHandler; -import io.reactivex.netty.channel.AbstractConnectionToChannelBridge.ReadProducer; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.Subscriber; -import rx.exceptions.MissingBackpressureException; -import rx.observers.Subscribers; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class ReadProducerTest { - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Rule - public final ProducerRule producerRule = new ProducerRule(); - - @Test(timeout = 60000) - public void testTurnOffBackpressureAtStart() throws Exception { - producerRule.producer.request(Long.MAX_VALUE); - - assertThat("Unexpected requested count.", producerRule.producer.getRequested(), is(Long.MAX_VALUE)); - - producerRule.producer.sendOnNext("Hello"); - - /*Backpressure turned off, don't decrement*/ - assertThat("Unexpected requested count.", producerRule.producer.getRequested(), is(Long.MAX_VALUE)); - } - - @Test(timeout = 60000) - public void testTurnOffBackpressureLater() throws Exception { - producerRule.producer.request(1); - assertThat("Unexpected requested count.", producerRule.producer.getRequested(), is(1L)); - - producerRule.producer.request(Long.MAX_VALUE); /*now turn off*/ - assertThat("Unexpected requested count.", producerRule.producer.getRequested(), is(Long.MAX_VALUE)); - - producerRule.producer.sendOnNext("Hello"); - - /*Backpressure turned off, don't decrement*/ - assertThat("Unexpected requested count.", producerRule.producer.getRequested(), is(Long.MAX_VALUE)); - } - - @Test(timeout = 60000) - public void testBackpressure() throws Exception { - producerRule.producer.request(1); - assertThat("Unexpected requested count.", producerRule.producer.getRequested(), is(1L)); - - producerRule.producer.sendOnNext("Hello"); - - /*Backpressure on, so decrement*/ - assertThat("Unexpected requested count.", producerRule.producer.getRequested(), is(0L)); - } - - @Test(timeout = 60000) - public void testRequestedOverflow() throws Exception { - producerRule.producer.request(1); - assertThat("Unexpected requested count.", producerRule.producer.getRequested(), is(1L)); - - producerRule.producer.request(Long.MAX_VALUE - 1); /*Adding overflows & turn off backpressure*/ - assertThat("Unexpected requested count.", producerRule.producer.getRequested(), is(Long.MAX_VALUE)); - - producerRule.producer.sendOnNext("Hello"); - - /*Backpressure turned off, don't decrement*/ - assertThat("Unexpected requested count.", producerRule.producer.getRequested(), is(Long.MAX_VALUE)); - } - - @Test(timeout = 60000) - public void testSupplyMoreThanDemand() throws Exception { - thrown.expectCause(isA(MissingBackpressureException.class)); - - producerRule.producer.request(1); - assertThat("Unexpected requested count.", producerRule.producer.getRequested(), is(1L)); - - producerRule.producer.sendOnNext("Hello"); - - /*Backpressure on, so decrement*/ - assertThat("Unexpected requested count.", producerRule.producer.getRequested(), is(0L)); - - producerRule.producer.sendOnNext("Hello"); /*overflow*/ - } - - @Test(timeout = 60000) - public void testShouldReadMoreOnLessDemand() throws Exception { - producerRule.producer.request(0); - assertThat("Unexpected requested count.", producerRule.producer.getRequested(), is(0L)); - assertThat("Unexpected read more status.", - producerRule.producer.shouldReadMore(producerRule.channel.pipeline().firstContext()), is(false)); - } - - @Test(timeout = 60000) - public void testShouldReadMoreOnUnsubscribe() throws Exception { - producerRule.producer.request(1); - assertThat("Unexpected requested count.", producerRule.producer.getRequested(), is(1L)); - assertThat("Unexpected read more status.", - producerRule.producer.shouldReadMore(producerRule.channel.pipeline().firstContext()), is(true)); - - producerRule.subscriber.unsubscribe(); - - assertThat("Unexpected read more status.", - producerRule.producer.shouldReadMore(producerRule.channel.pipeline().firstContext()), is(false)); - } - - public static class ProducerRule extends ExternalResource { - - private ReadProducer producer; - private EmbeddedChannel channel; - private Subscriber subscriber; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - channel = new EmbeddedChannel(new LoggingHandler()); - subscriber = Subscribers.empty(); - producer = new ReadProducer<>(subscriber, channel); - base.evaluate(); - } - }; - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/channel/SubscriberToChannelFutureBridgeTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/channel/SubscriberToChannelFutureBridgeTest.java deleted file mode 100644 index 960f46e..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/channel/SubscriberToChannelFutureBridgeTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.ChannelFuture; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.mockito.Mockito; -import rx.observers.TestSubscriber; - -public class SubscriberToChannelFutureBridgeTest { - - @Rule - public final BridgeRule rule = new BridgeRule(); - - @Test(timeout = 60000) - public void testBridge() throws Exception { - - rule.subscriber.unsubscribe(); - - Mockito.verify(rule.future).addListener(rule.bridge); - Mockito.verify(rule.future).removeListener(rule.bridge); - } - - @Test(timeout = 60000) - public void testSuccess() throws Exception { - rule.completeFuture(); - rule.subscriber.assertTerminalEvent(); - rule.subscriber.assertNoErrors(); - } - - @Test(timeout = 60000) - public void testError() throws Exception { - rule.failFuture(); - rule.subscriber.assertTerminalEvent(); - rule.subscriber.assertError(IllegalStateException.class); - } - - public static class BridgeRule extends ExternalResource { - - public TestSubscriber subscriber; - public SubscriberToChannelFutureBridge bridge; - private ChannelFuture future; - private volatile boolean futureTerminated; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - subscriber = new TestSubscriber<>(); - bridge = new SubscriberToChannelFutureBridge() { - - @Override - protected void doOnSuccess(ChannelFuture future) { - subscriber.onCompleted(); - } - - @Override - protected void doOnFailure(ChannelFuture future, Throwable cause) { - subscriber.onError(cause); - } - }; - - future = Mockito.mock(ChannelFuture.class); - - bridge.bridge(future, subscriber); - base.evaluate(); - } - }; - } - - public void completeFuture() throws Exception { - if (futureTerminated) { - throw new IllegalStateException("Channel future is already terminated"); - } - futureTerminated = true; - Mockito.when(future.isSuccess()).thenReturn(true); - bridge.operationComplete(future); - } - - public void failFuture() throws Exception { - if (futureTerminated) { - throw new IllegalStateException("Channel future is already terminated"); - } - futureTerminated = true; - Mockito.when(future.isSuccess()).thenReturn(false); - Mockito.when(future.cause()).thenReturn(new IllegalStateException("Force terminate")); - bridge.operationComplete(future); - } - } - -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/channel/WriteStreamSubscriberTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/channel/WriteStreamSubscriberTest.java deleted file mode 100644 index 1a71a57..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/channel/WriteStreamSubscriberTest.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.logging.LoggingHandler; -import io.reactivex.netty.channel.BackpressureManagingHandler.BytesWriteInterceptor; -import io.reactivex.netty.channel.BackpressureManagingHandler.WriteStreamSubscriber; -import io.reactivex.netty.test.util.MockProducer; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import java.io.IOException; -import java.util.Queue; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class WriteStreamSubscriberTest { - - @Rule - public final SubscriberRule subscriberRule = new SubscriberRule(); - - @Test(timeout = 60000) - public void testOnStart() throws Exception { - assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(false)); - subscriberRule.start(); - assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(false)); - - assertThat("Unexpected request made to the producer.", subscriberRule.mockProducer.getRequested(), - is(subscriberRule.defaultRequestN())); - } - - @Test(timeout = 60000) - public void testUnsubscribeOnPromiseCancel() throws Exception { - subscriberRule.start(); - - assertThat("Subsriber isn't subscribed.", subscriberRule.subscriber.isUnsubscribed(), is(false)); - - subscriberRule.channelPromise.cancel(false); - - assertThat("Promise not cancelled.", subscriberRule.channelPromise.isCancelled(), is(true)); - - assertThat("Subsriber isn't unsubscribed.", subscriberRule.subscriber.isUnsubscribed(), is(true)); - } - - @Test(timeout = 60000) - public void testWriteCompleteBeforeStream() throws Exception { - subscriberRule.start(); - - String msg1 = "msg1"; - subscriberRule.writeAndFlushMessages(msg1); - subscriberRule.assertMessagesWritten(msg1); - - assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(false)); - - subscriberRule.subscriber.onCompleted(); - - assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(true)); - assertThat("Unexpected promise result.", subscriberRule.channelPromise.isSuccess(), is(true)); - } - - @Test(timeout = 60000) - public void testWriteCompleteAfterStream() throws Exception { - subscriberRule.start(); - - String msg1 = "msg1"; - subscriberRule.writeMessages(msg1); - assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(false)); - subscriberRule.subscriber.onCompleted(); - /*Complete when write completes.*/ - assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(false)); - - subscriberRule.channel.flush(); /*Completes write*/ - - subscriberRule.assertMessagesWritten(msg1); - - assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(true)); - assertThat("Unexpected promise result.", subscriberRule.channelPromise.isSuccess(), is(true)); - } - - @Test(timeout = 60000) - public void testMultiWrite() throws Exception { - subscriberRule.start(); - - String msg1 = "msg1"; - String msg2 = "msg2"; - subscriberRule.writeMessages(msg1, msg2); - assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(false)); - subscriberRule.subscriber.onCompleted(); - /*Complete when write completes.*/ - assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(false)); - - subscriberRule.channel.flush(); /*Completes write*/ - - subscriberRule.assertMessagesWritten(msg1, msg2); - - assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(true)); - assertThat("Unexpected promise result.", subscriberRule.channelPromise.isSuccess(), is(true)); - } - - @Test(timeout = 60000) - public void testWriteFailed() throws Exception { - subscriberRule.start(); - - String msg1 = "msg1"; - subscriberRule.writeMessages(msg1); - assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(false)); - - subscriberRule.channel.close(); - - assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(true)); - assertThat("Unexpected promise result.", subscriberRule.channelPromise.isSuccess(), is(false)); - } - - @Test(timeout = 60000) - public void testStreamError() throws Exception { - subscriberRule.start(); - - subscriberRule.sendMessagesAndAssert(1); - - subscriberRule.subscriber.onError(new IOException()); - - assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(true)); - assertThat("Unexpected promise result.", subscriberRule.channelPromise.isSuccess(), is(false)); - } - - @Test(timeout = 60000) - public void testRequestMoreNotRequired() throws Exception { - subscriberRule.init(4); - subscriberRule.start(); - - assertThat("Unexpected request made to the producer.", subscriberRule.mockProducer.getRequested(), - is(subscriberRule.defaultRequestN())); - - subscriberRule.sendMessagesAndAssert(2); // Pending: 4 - 2 : low water mark: 4/2 - subscriberRule.subscriber.requestMoreIfNeeded(subscriberRule.defaultRequestN); - - assertThat("Unexpected request made to the producer.", subscriberRule.mockProducer.getRequested(), - is(subscriberRule.defaultRequestN())); - } - - @Test(timeout = 60000) - public void testRequestMoreRequired() throws Exception { - subscriberRule.init(4); - subscriberRule.start(); - - assertThat("Unexpected request made to the producer.", subscriberRule.mockProducer.getRequested(), - is(subscriberRule.defaultRequestN())); - - subscriberRule.sendMessagesAndAssert(3); // Pending: 4 - 3 : low water mark: 4/2 - subscriberRule.subscriber.requestMoreIfNeeded(subscriberRule.defaultRequestN); - - assertThat("Unexpected request made to the producer.", subscriberRule.mockProducer.getRequested(), - is(7L));// request: 4 + 4 - (4 - 3) - } - - @Test(timeout = 60000) - public void testLowerMaxBufferSize() throws Exception { - subscriberRule.init(4); - subscriberRule.start(); - - subscriberRule.subscriber.requestMoreIfNeeded(2); - - assertThat("Unexpected request made to the producer.", subscriberRule.mockProducer.getRequested(), - is(subscriberRule.defaultRequestN())); - } - - @Test(timeout = 60000) - public void testLowerMaxBufferSizeAndThenMore() throws Exception { - subscriberRule.init(8); - subscriberRule.start(); - - subscriberRule.subscriber.requestMoreIfNeeded(6); - subscriberRule.sendMessagesAndAssert(6); // Pending: 8 - 6 : low water mark: 6/2 - subscriberRule.subscriber.requestMoreIfNeeded(6); - - assertThat("Unexpected request made to the producer.", subscriberRule.mockProducer.getRequested(), - is(12L)); // requestN: 8 + 6 - (8 - 6) - } - - @Test(timeout = 60000) - public void testHigherMaxBufferSize() throws Exception { - subscriberRule.init(4); - subscriberRule.start(); - - subscriberRule.subscriber.requestMoreIfNeeded(6); - - assertThat("Unexpected request made to the producer.", subscriberRule.mockProducer.getRequested(), - is(6L)); // requestN: 4 + 6 - 4 - } - - public static class SubscriberRule extends ExternalResource { - - private WriteStreamSubscriber subscriber; - private ChannelPromise channelPromise; - private EmbeddedChannel channel; - private MockProducer mockProducer; - private int defaultRequestN; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - init(BytesWriteInterceptor.MAX_PER_SUBSCRIBER_REQUEST); - base.evaluate(); - } - }; - } - - protected void init(int defaultRequestN) { - this.defaultRequestN = defaultRequestN; - channel = new EmbeddedChannel(new LoggingHandler()); - channelPromise = channel.newPromise(); - ChannelHandlerContext ctx = channel.pipeline().firstContext(); - subscriber = new WriteStreamSubscriber(ctx, channelPromise, defaultRequestN().intValue()); - mockProducer = new MockProducer(); - } - - public void start() { - subscriber.onStart(); /*So that setProducer does not request Long.MAX_VALUE*/ - subscriber.setProducer(mockProducer); - - mockProducer.assertBackpressureRequested(); - mockProducer.assertIllegalRequest(); - } - - public void writeAndFlushMessages(Object... msgs) { - writeMessages(msgs); - channel.flush(); - } - - public void writeMessages(Object... msgs) { - for (Object msg : msgs) { - subscriber.onNext(msg); - } - } - - public void assertMessagesWritten(Object... msgs) { - Queue outboundMessages = channel.outboundMessages(); - - if (null == msgs || msgs.length == 0) { - assertThat("Unexpected number of messages written on the channel.", outboundMessages, is(empty())); - return; - } - - assertThat("Unexpected number of messages written on the channel.", outboundMessages, hasSize(msgs.length)); - assertThat("Unexpected messages written on the channel.", outboundMessages, contains(msgs)); - } - - protected void sendMessagesAndAssert(int count) { - String[] msgs = new String[count]; - for (int i = 0; i < count; i++) { - msgs[i] = "msg" + i; - } - writeAndFlushMessages(msgs); - assertThat("Unexpected promise completion state.", channelPromise.isDone(), is(false)); - assertMessagesWritten(msgs); - } - - public Long defaultRequestN() { - return Long.valueOf(defaultRequestN); - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/channel/WriteTransformerTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/channel/WriteTransformerTest.java deleted file mode 100644 index f454a8b..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/channel/WriteTransformerTest.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.channel; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import java.nio.charset.Charset; -import java.util.Arrays; -import java.util.List; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class WriteTransformerTest { - - @Rule - public final ConverterRule converterRule = new ConverterRule(); - - @Test(timeout = 60000) - public void testWriteString() throws Exception { - String msg = "Hello"; - converterRule.channel.writeAndFlush(msg); - - ByteBuf written = converterRule.readNextOutboundBuffer(); - assertThat("Unexpected content of buffer written.", written.toString(Charset.defaultCharset()), equalTo(msg)); - } - - @Test(timeout = 60000) - public void testWriteByteArray() throws Exception { - byte[] msg = "Hello".getBytes(); - converterRule.channel.writeAndFlush(msg); - ByteBuf writtenMsg = converterRule.readNextOutboundBuffer(); - byte[] asBytes = new byte[msg.length]; - writtenMsg.readBytes(asBytes); - - assertThat("Unexpected content of buffer written.", asBytes, equalTo(msg)); - } - - @Test(timeout = 60000) - public void testTransformerSingle() throws Exception { - converterRule.appendTransformer(new AllocatingTransformer() { - @Override - public List transform(Integer toTransform, ByteBufAllocator allocator) { - return Arrays.asList(allocator.buffer().writeInt(toTransform), - allocator.buffer().writeInt(++toTransform)); - } - }); - - converterRule.channel.writeAndFlush(1); - ByteBuf written = converterRule.readNextOutboundBuffer(2); - - assertThat("Unexpected message written on the channel", written.readInt(), is(1)); - - written = converterRule.readNextOutboundBuffer(); - assertThat("Unexpected message written on the channel", written.readInt(), is(2)); - } - - @Test(timeout = 60000) - public void testTransformerChained() throws Exception { - converterRule.appendTransformer(new AllocatingTransformer() { - @Override - public List transform(Integer toTransform, ByteBufAllocator allocator) { - return Arrays.asList(allocator.buffer().writeInt(toTransform), - allocator.buffer().writeInt(++toTransform)); - } - }); - - converterRule.appendTransformer(new AllocatingTransformer() { - @Override - public List transform(Long toTransform, ByteBufAllocator allocator) { - int i = toTransform.intValue(); - return Arrays.asList(i, ++i); - } - }); - - converterRule.channel.writeAndFlush(1L); - ByteBuf written = converterRule.readNextOutboundBuffer(4); - - assertThat("Unexpected message written on the channel", written.readInt(), is(1)); - - written = converterRule.readNextOutboundBuffer(3); - assertThat("Unexpected message written on the channel", written.readInt(), is(2)); - - written = converterRule.readNextOutboundBuffer(2); - assertThat("Unexpected message written on the channel", written.readInt(), is(2)); - - written = converterRule.readNextOutboundBuffer(1); - assertThat("Unexpected message written on the channel", written.readInt(), is(3)); - } - - public static class ConverterRule extends ExternalResource { - - private WriteTransformer converter; - private EmbeddedChannel channel; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - converter = new WriteTransformer(); - channel = new EmbeddedChannel(converter); - base.evaluate(); - } - }; - } - - public ByteBuf readNextOutboundBuffer() { - return readNextOutboundBuffer(1); - } - - public ByteBuf readNextOutboundBuffer(int expectedWrittenMessages) { - assertThat("Unexpected outbound messages size.", channel.outboundMessages(), - hasSize(expectedWrittenMessages)); - Object writtenMsg = channel.readOutbound(); - assertThat("Unexpected message type written on the channel.", writtenMsg, is(instanceOf(ByteBuf.class))); - return (ByteBuf) writtenMsg; - } - - public void appendTransformer(AllocatingTransformer transformer) { - @SuppressWarnings({"rawtypes", "unchecked"}) - AppendTransformerEvent event = new AppendTransformerEvent(transformer); - channel.pipeline().fireUserEventTriggered(event); - - } - } - -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/channel/events/ConnectionEventPublisherTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/channel/events/ConnectionEventPublisherTest.java deleted file mode 100644 index aa2c6f2..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/channel/events/ConnectionEventPublisherTest.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.channel.events; - -import io.reactivex.netty.test.util.MockConnectionEventListener; -import io.reactivex.netty.test.util.MockConnectionEventListener.Event; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class ConnectionEventPublisherTest { - - @Rule - public final PublisherRule rule = new PublisherRule(); - - @Test(timeout = 60000) - public void testOnByteRead() throws Exception { - rule.publisher.onByteRead(1); - rule.listener.assertMethodsCalled(Event.BytesRead); - - assertThat("Listener not called with bytes read.", rule.listener.getBytesRead(), is(1L)); - } - - @Test(timeout = 60000) - public void testOnByteWritten() throws Exception { - rule.publisher.onByteWritten(1); - rule.listener.assertMethodsCalled(Event.BytesWritten); - - assertThat("Listener not called with bytes written.", rule.listener.getBytesWritten(), is(1L)); - } - - @Test(timeout = 60000) - public void testOnFlushStart() throws Exception { - rule.publisher.onFlushStart(); - rule.listener.assertMethodsCalled(Event.FlushStart); - } - - @Test(timeout = 60000) - public void testOnFlushSuccess() throws Exception { - rule.publisher.onFlushComplete(1, TimeUnit.MILLISECONDS); - rule.listener.assertMethodsCalled(Event.FlushSuccess); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testOnWriteStart() throws Exception { - rule.publisher.onWriteStart(); - rule.listener.assertMethodsCalled(Event.WriteStart); - } - - @Test(timeout = 60000) - public void testOnWriteSuccess() throws Exception { - rule.publisher.onWriteSuccess(1, TimeUnit.MILLISECONDS); - rule.listener.assertMethodsCalled(Event.WriteSuccess); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testOnWriteFailed() throws Exception { - final Throwable expected = new NullPointerException("Deliberate"); - rule.publisher.onWriteFailed(1, TimeUnit.MILLISECONDS, expected); - rule.listener.assertMethodsCalled(Event.WriteFailed); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - assertThat("Listener not called with error.", rule.listener.getRecievedError(), is(expected)); - } - - @Test(timeout = 60000) - public void testOnConnectionCloseStart() throws Exception { - rule.publisher.onConnectionCloseStart(); - rule.listener.assertMethodsCalled(Event.CloseStart); - } - - @Test(timeout = 60000) - public void testOnConnectionCloseSuccess() throws Exception { - rule.publisher.onConnectionCloseSuccess(1, TimeUnit.MILLISECONDS); - rule.listener.assertMethodsCalled(Event.CloseSuccess); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testOnConnectionCloseFailed() throws Exception { - final Throwable expected = new NullPointerException("Deliberate"); - rule.publisher.onConnectionCloseFailed(1, TimeUnit.MILLISECONDS, expected); - rule.listener.assertMethodsCalled(Event.CloseFailed); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - assertThat("Listener not called with error.", rule.listener.getRecievedError(), is(expected)); - } - - @Test(timeout = 60000) - public void testCustomEvent() throws Exception { - Object event = "Hello"; - rule.publisher.onCustomEvent(event); - rule.listener.assertMethodsCalled(Event.CustomEvent); - - assertThat("Listener not called with event.", rule.listener.getCustomEvent(), is(event)); - } - - @Test(timeout = 60000) - public void testCustomEventWithError() throws Exception { - final Throwable expected = new NullPointerException("Deliberate"); - Object event = "Hello"; - rule.publisher.onCustomEvent(event, expected); - rule.listener.assertMethodsCalled(Event.CustomEventWithError); - - assertThat("Listener not called with event.", rule.listener.getCustomEvent(), is(event)); - assertThat("Listener not called with error.", rule.listener.getRecievedError(), is(expected)); - } - - @Test(timeout = 60000) - public void testCustomEventWithDuration() throws Exception { - Object event = "Hello"; - rule.publisher.onCustomEvent(event, 1, TimeUnit.MILLISECONDS); - rule.listener.assertMethodsCalled(Event.CustomEventWithDuration); - - assertThat("Listener not called with event.", rule.listener.getCustomEvent(), is(event)); - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testCustomEventWithDurationAndError() throws Exception { - final Throwable expected = new NullPointerException("Deliberate"); - Object event = "Hello"; - rule.publisher.onCustomEvent(event, 1, TimeUnit.MILLISECONDS, expected); - rule.listener.assertMethodsCalled(Event.CustomEventWithDurationAndError); - - assertThat("Listener not called with event.", rule.listener.getCustomEvent(), is(event)); - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - assertThat("Listener not called with error.", rule.listener.getRecievedError(), is(expected)); - } - - @Test(timeout = 60000) - public void testPublishingEnabled() throws Exception { - assertThat("Publishing not enabled.", rule.publisher.publishingEnabled(), is(true)); - } - - @Test(timeout = 60000) - public void testCopy() throws Exception { - ConnectionEventPublisher copy = rule.publisher.copy(); - - assertThat("Publisher not copied.", copy, is(not(sameInstance(rule.publisher)))); - assertThat("Listeners not copied.", copy.getListeners(), is(not(sameInstance(rule.publisher.getListeners())))); - } - - public static class PublisherRule extends ExternalResource { - - private MockConnectionEventListener listener; - private ConnectionEventPublisher publisher; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - listener = new MockConnectionEventListener(); - publisher = new ConnectionEventPublisher<>(); - publisher.subscribe(listener); - base.evaluate(); - } - }; - } - } - -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/client/ClientStateTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/client/ClientStateTest.java deleted file mode 100644 index 3a98b82..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/client/ClientStateTest.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client; - -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.handler.logging.LogLevel; -import io.reactivex.netty.HandlerNames; -import io.reactivex.netty.channel.DetachedChannelPipeline; -import io.reactivex.netty.test.util.embedded.EmbeddedConnectionProvider; -import io.reactivex.netty.util.LoggingHandlerFactory; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.mockito.Matchers; -import org.mockito.Mockito; -import rx.Observable; -import rx.functions.Action1; -import rx.functions.Func0; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class ClientStateTest { - - @Rule - public final ClientStateRule clientStateRule = new ClientStateRule(); - - @Test(timeout = 60000) - public void testAddChannelHandlerFirst() throws Exception { - String handlerName = "test_handler"; - Func0 handlerFactory = clientStateRule.newHandler(); - - ClientState newState = clientStateRule.clientState - .addChannelHandlerFirst(handlerName, handlerFactory); - - clientStateRule.verifyMockPipelineAccessPostCopy(); - assertThat("Client state not copied.", clientStateRule.clientState, is(not(newState))); - assertThat("Options copied.", clientStateRule.clientState.unsafeChannelOptions(), - is(newState.unsafeChannelOptions())); - assertThat("Detached pipeline not copied.", clientStateRule.clientState.unsafeDetachedPipeline(), - is(not(newState.unsafeDetachedPipeline()))); - - Mockito.verify(newState.unsafeDetachedPipeline()).addFirst(handlerName, handlerFactory); - - Mockito.verifyNoMoreInteractions(newState.unsafeDetachedPipeline()); - Mockito.verifyNoMoreInteractions(clientStateRule.mockPipeline); - } - - @Test(timeout = 60000) - public void testAddChannelHandlerFirstWithEventExecGroup() throws Exception { - String handlerName = "test_handler"; - Func0 handlerFactory = clientStateRule.newHandler(); - NioEventLoopGroup executor = new NioEventLoopGroup(); - - ClientState newState = clientStateRule.clientState - .addChannelHandlerFirst(executor, handlerName, handlerFactory); - - clientStateRule.verifyMockPipelineAccessPostCopy(); - assertThat("Client state not copied.", clientStateRule.clientState, is(not(newState))); - assertThat("Options copied.", clientStateRule.clientState.unsafeChannelOptions(), - is(newState.unsafeChannelOptions())); - assertThat("Detached pipeline not copied.", clientStateRule.clientState.unsafeDetachedPipeline(), - is(not(newState.unsafeDetachedPipeline()))); - - Mockito.verify(newState.unsafeDetachedPipeline()).addFirst(executor, handlerName, handlerFactory); - - Mockito.verifyNoMoreInteractions(newState.unsafeDetachedPipeline()); - Mockito.verifyNoMoreInteractions(clientStateRule.mockPipeline); - } - - @Test(timeout = 60000) - public void testAddChannelHandlerLast() throws Exception { - String handlerName = "test_handler"; - Func0 handlerFactory = clientStateRule.newHandler(); - - ClientState newState = clientStateRule.clientState - .addChannelHandlerLast(handlerName, handlerFactory); - - clientStateRule.verifyMockPipelineAccessPostCopy(); - assertThat("Client state not copied.", clientStateRule.clientState, is(not(newState))); - assertThat("Options copied.", clientStateRule.clientState.unsafeChannelOptions(), - is(newState.unsafeChannelOptions())); - assertThat("Detached pipeline not copied.", clientStateRule.clientState.unsafeDetachedPipeline(), - is(not(newState.unsafeDetachedPipeline()))); - - Mockito.verify(newState.unsafeDetachedPipeline()).addLast(handlerName, handlerFactory); - - Mockito.verifyNoMoreInteractions(newState.unsafeDetachedPipeline()); - Mockito.verifyNoMoreInteractions(clientStateRule.mockPipeline); - } - - @Test(timeout = 60000) - public void testAddChannelHandlerLastWithEventExecGroup() throws Exception { - String handlerName = "test_handler"; - Func0 handlerFactory = clientStateRule.newHandler(); - NioEventLoopGroup executor = new NioEventLoopGroup(); - - ClientState newState = clientStateRule.clientState - .addChannelHandlerLast(executor, handlerName, handlerFactory); - - clientStateRule.verifyMockPipelineAccessPostCopy(); - assertThat("Client state not copied.", clientStateRule.clientState, is(not(newState))); - assertThat("Options copied.", clientStateRule.clientState.unsafeChannelOptions(), - is(newState.unsafeChannelOptions())); - assertThat("Detached pipeline not copied.", clientStateRule.clientState.unsafeDetachedPipeline(), - is(not(newState.unsafeDetachedPipeline()))); - - Mockito.verify(newState.unsafeDetachedPipeline()).addLast(executor, handlerName, handlerFactory); - - Mockito.verifyNoMoreInteractions(newState.unsafeDetachedPipeline()); - Mockito.verifyNoMoreInteractions(clientStateRule.mockPipeline); - } - - @Test(timeout = 60000) - public void testAddChannelHandlerBefore() throws Exception { - String handlerName = "test_handler"; - String baseHandlerName = "test_handler_base"; - Func0 handlerFactory = clientStateRule.newHandler(); - - ClientState newState = clientStateRule.clientState - .addChannelHandlerBefore(baseHandlerName, handlerName, handlerFactory); - - clientStateRule.verifyMockPipelineAccessPostCopy(); - assertThat("Client state not copied.", clientStateRule.clientState, is(not(newState))); - assertThat("Options copied.", clientStateRule.clientState.unsafeChannelOptions(), - is(newState.unsafeChannelOptions())); - assertThat("Detached pipeline not copied.", clientStateRule.clientState.unsafeDetachedPipeline(), - is(not(newState.unsafeDetachedPipeline()))); - - Mockito.verify(newState.unsafeDetachedPipeline()).addBefore(baseHandlerName, handlerName, handlerFactory); - - Mockito.verifyNoMoreInteractions(newState.unsafeDetachedPipeline()); - Mockito.verifyNoMoreInteractions(clientStateRule.mockPipeline); - } - - @Test(timeout = 60000) - public void testAddChannelHandlerBeforeWithEventExecGroup() throws Exception { - String handlerName = "test_handler"; - String baseHandlerName = "test_handler_base"; - Func0 handlerFactory = clientStateRule.newHandler(); - NioEventLoopGroup executor = new NioEventLoopGroup(); - ClientState newState = clientStateRule.clientState - .addChannelHandlerBefore(executor, baseHandlerName, handlerName, handlerFactory); - - clientStateRule.verifyMockPipelineAccessPostCopy(); - assertThat("Client state not copied.", clientStateRule.clientState, is(not(newState))); - assertThat("Options copied.", clientStateRule.clientState.unsafeChannelOptions(), - is(newState.unsafeChannelOptions())); - assertThat("Detached pipeline not copied.", clientStateRule.clientState.unsafeDetachedPipeline(), - is(not(newState.unsafeDetachedPipeline()))); - - Mockito.verify(newState.unsafeDetachedPipeline()).addBefore(executor, baseHandlerName, handlerName, - handlerFactory); - - Mockito.verifyNoMoreInteractions(newState.unsafeDetachedPipeline()); - Mockito.verifyNoMoreInteractions(clientStateRule.mockPipeline); - } - - @Test(timeout = 60000) - public void testAddChannelHandlerAfter() throws Exception { - String handlerName = "test_handler"; - String baseHandlerName = "test_handler_base"; - Func0 handlerFactory = clientStateRule.newHandler(); - ClientState newState = clientStateRule.clientState - .addChannelHandlerAfter(baseHandlerName, handlerName, handlerFactory); - - clientStateRule.verifyMockPipelineAccessPostCopy(); - assertThat("Client state not copied.", clientStateRule.clientState, is(not(newState))); - assertThat("Options copied.", clientStateRule.clientState.unsafeChannelOptions(), - is(newState.unsafeChannelOptions())); - assertThat("Detached pipeline not copied.", clientStateRule.clientState.unsafeDetachedPipeline(), - is(not(newState.unsafeDetachedPipeline()))); - - Mockito.verify(newState.unsafeDetachedPipeline()).addAfter(baseHandlerName, handlerName, handlerFactory); - - Mockito.verifyNoMoreInteractions(newState.unsafeDetachedPipeline()); - Mockito.verifyNoMoreInteractions(clientStateRule.mockPipeline); - - } - - @Test(timeout = 60000) - public void testAddChannelHandlerAfterWithEventExecGroup() throws Exception { - String handlerName = "test_handler"; - String baseHandlerName = "test_handler_base"; - Func0 handlerFactory = clientStateRule.newHandler(); - NioEventLoopGroup executor = new NioEventLoopGroup(); - ClientState newState = clientStateRule.clientState - .addChannelHandlerAfter(executor, baseHandlerName, handlerName, handlerFactory); - - clientStateRule.verifyMockPipelineAccessPostCopy(); - assertThat("Client state not copied.", clientStateRule.clientState, is(not(newState))); - assertThat("Options copied.", clientStateRule.clientState.unsafeChannelOptions(), - is(newState.unsafeChannelOptions())); - assertThat("Detached pipeline not copied.", clientStateRule.clientState.unsafeDetachedPipeline(), - is(not(newState.unsafeDetachedPipeline()))); - - Mockito.verify(newState.unsafeDetachedPipeline()).addAfter(executor, baseHandlerName, handlerName, - handlerFactory); - - Mockito.verifyNoMoreInteractions(newState.unsafeDetachedPipeline()); - Mockito.verifyNoMoreInteractions(clientStateRule.mockPipeline); - - } - - @Test(timeout = 60000) - public void testPipelineConfigurator() throws Exception { - final Action1 pipelineConfigurator = new Action1() { - @Override - public void call(ChannelPipeline pipeline) { - } - }; - - ClientState newState = clientStateRule.clientState.pipelineConfigurator(pipelineConfigurator); - - clientStateRule.verifyMockPipelineAccessPostCopy(); - assertThat("Client state not copied.", clientStateRule.clientState, is(not(newState))); - assertThat("Options copied.", clientStateRule.clientState.unsafeChannelOptions(), - is(newState.unsafeChannelOptions())); - assertThat("Detached pipeline not copied.", clientStateRule.clientState.unsafeDetachedPipeline(), - is(not(newState.unsafeDetachedPipeline()))); - - Mockito.verify(newState.unsafeDetachedPipeline()).configure(pipelineConfigurator); - - Mockito.verifyNoMoreInteractions(newState.unsafeDetachedPipeline()); - Mockito.verifyNoMoreInteractions(clientStateRule.mockPipeline); - } - - @Test(timeout = 60000) - public void testEnableWireLogging() throws Exception { - ClientState newState = clientStateRule.clientState.enableWireLogging("", LogLevel.ERROR); - - clientStateRule.verifyMockPipelineAccessPostCopy(); - assertThat("Client state not copied.", clientStateRule.clientState, is(not(newState))); - assertThat("Options copied.", clientStateRule.clientState.unsafeChannelOptions(), - is(newState.unsafeChannelOptions())); - assertThat("Detached pipeline not copied.", clientStateRule.clientState.unsafeDetachedPipeline(), - is(not(newState.unsafeDetachedPipeline()))); - - Mockito.verify(newState.unsafeDetachedPipeline()) - .addFirst(HandlerNames.WireLogging.getName(), LoggingHandlerFactory.getFactory("", LogLevel.ERROR)); - - Mockito.verifyNoMoreInteractions(newState.unsafeDetachedPipeline()); - Mockito.verifyNoMoreInteractions(clientStateRule.mockPipeline); - } - - public static class ClientStateRule extends ExternalResource { - - private ClientState clientState; - private DetachedChannelPipeline mockPipeline; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - mockPipeline = Mockito.mock(DetachedChannelPipeline.class, Mockito.RETURNS_MOCKS); - EmbeddedConnectionProvider ecp = new EmbeddedConnectionProvider<>(); - clientState = ClientState.create(mockPipeline, ecp.asFactory(), Observable.empty()) - .enableWireLogging(LogLevel.ERROR); - base.evaluate(); - } - }; - } - - public Func0 newHandler() { - return new Func0() { - @Override - public ChannelHandler call() { - return new TestableChannelHandler(); - } - }; - } - - public void verifyMockPipelineAccessPostCopy() { - Mockito.verify(mockPipeline).copy(Matchers.>anyObject()); - } - - public ClientState updateState(ClientState newState) { - final ClientState current = clientState; - clientState = newState; - return current; - } - - public static class TestableChannelHandler extends ChannelDuplexHandler { - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/client/SslClientTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/client/SslClientTest.java deleted file mode 100644 index ae04239..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/client/SslClientTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client; - -import org.junit.Test; - -public class SslClientTest { - - @Test(timeout = 60000) - public void testReleaseOnSslFailure() throws Exception { - //TODO: Fix me -/* - HttpServer server = - HttpServer.newServer() - .start(new RequestHandler() { - @Override - public Observable handle(HttpServerRequest request, - HttpServerResponse response) { - return Observable.empty(); - } - }); - final SocketAddress serverAddress = new InetSocketAddress("127.0.0.1", server.getServerPort()); - - MockPoolLimitDeterminationStrategy strategy = new MockPoolLimitDeterminationStrategy(1); - - // The connect fails because the server does not support SSL. - TestSubscriber subscriber = new TestSubscriber<>(); - final PoolConfig config = new PoolConfig<>(); - config.limitDeterminationStrategy(strategy); - - ConnectionProvider connectionProvider = PooledConnectionProvider.create(config, serverAddress); - - HttpClient.newClient(connectionProvider) - .unsafeSecure() - .createGet("/") - .flatMap(new Func1, Observable>() { - @Override - public Observable call(HttpClientResponse response) { - return response.getContent(); - } - }) - .subscribe(subscriber); - - subscriber.awaitTerminalEvent(); - - assertThat("Unexpected error notifications.", subscriber.getOnErrorEvents(), hasSize(1)); - assertThat("Unexpected error.", subscriber.getOnErrorEvents().get(0), is(instanceOf(SSLException.class))); - - Assert.assertEquals("Unexpected acquire counts.", 1, strategy.getAcquireCount()); - Assert.assertEquals("Unexpected release counts.", 1, strategy.getReleaseCount()); - Assert.assertEquals("Unexpected available permits.", 1, strategy.getAvailablePermits()); -*/ - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/client/loadbalancer/AbstractP2CStrategyTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/client/loadbalancer/AbstractP2CStrategyTest.java deleted file mode 100644 index 6ff4c18..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/client/loadbalancer/AbstractP2CStrategyTest.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client.loadbalancer; - -import io.netty.buffer.ByteBuf; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.client.ConnectionProvider; -import io.reactivex.netty.client.Host; -import io.reactivex.netty.client.HostConnector; -import io.reactivex.netty.client.events.ClientEventListener; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.test.util.MockEventPublisher; -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.Observable; -import rx.observers.TestSubscriber; - -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class AbstractP2CStrategyTest { - - public static final int UNUSABLE_HOST_WEIGHT = -1; - @Rule - public final StrategyRule rule = new StrategyRule(); - - @Test - public void testNoHosts() { - ConnectionProvider cp = rule.strategy.newStrategy( - Collections.>emptyList()); - TestSubscriber> sub = new TestSubscriber<>(); - cp.newConnectionRequest().subscribe(sub); - - sub.awaitTerminalEvent(); - sub.assertError(NoHostsAvailableException.class); - Assert.assertEquals("Unexpected number of hosts in the pool.", 0, rule.strategy.hostsInPool); - Assert.assertEquals("Unexpected number of no usable hosts count.", 1, rule.strategy.allUnusable); - } - - @Test - public void testSingleUnusableHost() { - ConnectionProvider cp = rule.strategy.newStrategy(rule.newHostStream(UNUSABLE_HOST_WEIGHT)); - TestSubscriber> sub = new TestSubscriber<>(); - cp.newConnectionRequest().subscribe(sub); - - sub.awaitTerminalEvent(); - sub.assertError(NoHostsAvailableException.class); - - Assert.assertEquals("Unexpected number of hosts in the pool.", 1, rule.strategy.hostsInPool); - Assert.assertEquals("Unexpected number of Unusable hosts found count.", 0, rule.strategy.twoUnusableHosts); - Assert.assertEquals("Unexpected number of no usable hosts count.", 1, rule.strategy.allUnusable); - } - - @Test - public void testMultipleUnusableHost() { - ConnectionProvider cp = rule.strategy.newStrategy(rule.newHostStream(UNUSABLE_HOST_WEIGHT, - UNUSABLE_HOST_WEIGHT)); - TestSubscriber> sub = new TestSubscriber<>(); - cp.newConnectionRequest().subscribe(sub); - - sub.awaitTerminalEvent(); - sub.assertError(NoHostsAvailableException.class); - - Assert.assertEquals("Unexpected number of hosts in the pool.", 2, rule.strategy.hostsInPool); - Assert.assertEquals("Unexpected number of Unusable hosts found count.", 5, rule.strategy.twoUnusableHosts); - Assert.assertEquals("Unexpected number of no usable hosts count.", 1, rule.strategy.allUnusable); - } - - @Test - public void testUsableAndUnusable() { - ConnectionProvider cp = rule.strategy.newStrategy(rule.newHostStream(10, UNUSABLE_HOST_WEIGHT)); - TestSubscriber> sub = new TestSubscriber<>(); - cp.newConnectionRequest().subscribe(sub); - - sub.awaitTerminalEvent(); - sub.assertNoErrors(); - - Assert.assertEquals("Unexpected number of hosts in the pool.", 2, rule.strategy.hostsInPool); - Assert.assertEquals("Unexpected number of Unusable hosts found count.", 0, rule.strategy.twoUnusableHosts); - Assert.assertEquals("Unexpected number of no usable hosts count.", 0,rule.strategy.allUnusable); - } - - public static class StrategyRule extends ExternalResource { - - private MockP2CStrategy strategy; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - strategy = new MockP2CStrategy(); - base.evaluate(); - } - }; - } - - public List> newHostStream(int... weights) { - List> toReturn = new ArrayList<>(); - for (int weight : weights) { - ConnectionProvider dummy = new ConnectionProvider() { - @Override - public Observable> newConnectionRequest() { - return Observable.empty(); - } - }; - Host h = new Host(new InetSocketAddress(0)); - EventPublisher publisher = MockEventPublisher.disabled(); - HostConnector connector = new HostConnector<>(h, dummy, null, publisher, null); - toReturn.add(new HostHolder<>(connector, new ClientListenerImpl(weight))); - } - return toReturn; - } - - private static class ClientListenerImpl extends ClientEventListener { - - private volatile double weight; - - public ClientListenerImpl(double weight) { - this.weight = weight; - } - - public double getWeight() { - return weight; - } - } - - private static class MockP2CStrategy extends AbstractP2CStrategy { - - private volatile int allUnusable; - private volatile int hostsInPool; - private volatile int twoUnusableHosts; - - @Override - protected ClientListenerImpl newListener(Host host) { - return new ClientListenerImpl(0); - } - - @Override - protected double getWeight(ClientListenerImpl listener) { - return listener.getWeight(); - } - - @Override - protected void noUsableHostsFound() { - allUnusable++; - } - - @Override - protected void foundTwoUnusableHosts() { - twoUnusableHosts++; - } - - @Override - protected void newHostsList(int size) { - hostsInPool += size; - } - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/client/loadbalancer/LoadBalancerFactoryTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/client/loadbalancer/LoadBalancerFactoryTest.java deleted file mode 100644 index 322bef6..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/client/loadbalancer/LoadBalancerFactoryTest.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client.loadbalancer; - -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.client.ConnectionProvider; -import io.reactivex.netty.client.Host; -import io.reactivex.netty.client.HostConnector; -import io.reactivex.netty.client.events.ClientEventListener; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.events.EventSource; -import org.hamcrest.Matchers; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.mockito.Mockito; -import rx.Observable; -import rx.functions.Func1; -import rx.observers.TestSubscriber; -import rx.subjects.PublishSubject; - -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.List; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class LoadBalancerFactoryTest { - - @Rule - public final LBFactoryRule rule = new LBFactoryRule(); - - @Test(timeout = 60000) - public void testHostRemove() throws Exception { - - TestSubscriber>> testSubscriber = rule.newHostsListListener(); - - rule.initProvider(); - Host host = rule.emitHost(); - testSubscriber.assertNoTerminalEvent(); - - assertThat("Unexpected number of hosts lists received", testSubscriber.getOnNextEvents(), hasSize(1)); - assertThat("Unexpected number of hosts received", testSubscriber.getOnNextEvents().get(0), hasSize(1)); - assertThat("Unexpected host received", testSubscriber.getOnNextEvents().get(0).get(0).getConnector().getHost(), - is(host)); - - rule.completeHost(host); - - assertThat("Unexpected number of hosts lists received", testSubscriber.getOnNextEvents(), hasSize(2)); - assertThat("Unexpected number of hosts received", testSubscriber.getOnNextEvents().get(1), - is(Matchers.>empty())); - } - - @Test(timeout = 60000) - public void testDuplicateHost() throws Exception { - TestSubscriber>> testSubscriber = rule.newHostsListListener(); - - rule.initProvider(); - Host host = rule.emitHost(); - testSubscriber.assertNoTerminalEvent(); - - assertThat("Unexpected number of hosts lists received", testSubscriber.getOnNextEvents(), hasSize(1)); - assertThat("Unexpected number of hosts received", testSubscriber.getOnNextEvents().get(0), hasSize(1)); - assertThat("Unexpected host received", testSubscriber.getOnNextEvents().get(0).get(0).getConnector().getHost(), - is(host)); - - rule.emitHost(host); - testSubscriber.assertNoTerminalEvent(); - - assertThat("Unexpected number of hosts lists received", testSubscriber.getOnNextEvents(), hasSize(1)); - assertThat("Unexpected number of hosts received", testSubscriber.getOnNextEvents().get(0), hasSize(1)); - assertThat("Unexpected host received", testSubscriber.getOnNextEvents().get(0).get(0).getConnector().getHost(), - is(host)); - } - - @Test(timeout = 60000) - public void testHostSourceEmitsError() throws Exception { - TestSubscriber>> testSubscriber = rule.newHostsListListener(); - - rule.initProvider(); - rule.emitHost(); - testSubscriber.assertNoTerminalEvent(); - - assertThat("Unexpected number of hosts lists received", testSubscriber.getOnNextEvents(), hasSize(1)); - - rule.hostStream.onError(new NullPointerException("Deliberate exception")); - - testSubscriber.assertNoTerminalEvent(); - assertThat("Unexpected number of hosts lists received", testSubscriber.getOnNextEvents(), hasSize(1)); - } - - public static class LBFactoryRule extends ExternalResource { - - private PublishSubject>> lists = PublishSubject.create(); - private PublishSubject hostStream = PublishSubject.create(); - private List emittedHosts = new ArrayList<>(); - private ConnectionProvider connectionProviderMock; - private EventSource eventSourceMock; - private EventPublisher eventPublisherMock; - private ClientEventListener eventListenerMock; - private LoadBalancerFactory factory; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - @SuppressWarnings("unchecked") - ConnectionProvider m = - (ConnectionProvider) Mockito.mock(ConnectionProvider.class); - connectionProviderMock = m; - - @SuppressWarnings("unchecked") - EventSource es = (EventSource)Mockito.mock(EventSource.class); - eventSourceMock = es; - - eventPublisherMock = Mockito.mock(EventPublisher.class); - eventListenerMock = Mockito.mock(ClientEventListener.class); - - factory = LoadBalancerFactory.create(new LoadBalancingStrategy() { - @Override - public ConnectionProvider newStrategy(List> hosts) { - lists.onNext(hosts); - return new ConnectionProvider() { - @Override - public Observable> newConnectionRequest() { - return Observable.empty(); - } - }; - } - - @Override - public HostHolder toHolder(HostConnector connector) { - return new HostHolder<>(connector, eventListenerMock); - } - }); - base.evaluate(); - } - }; - } - - public ConnectionProvider initProvider() { - return factory.newProvider(hostStream.map(new Func1>() { - @Override - public HostConnector call(Host host) { - return new HostConnector<>(host, connectionProviderMock, eventSourceMock, eventPublisherMock, - eventListenerMock); - } - })); - } - - public TestSubscriber>> newHostsListListener() { - TestSubscriber>> testSubscriber = new TestSubscriber<>(); - lists.subscribe(testSubscriber); - return testSubscriber; - } - - public Host emitHost() { - Host host = new Host(new InetSocketAddress(0), PublishSubject.create()); - emittedHosts.add(host); - hostStream.onNext(host); - return host; - } - - public void emitHost(Host host) { - emittedHosts.add(host); - hostStream.onNext(host); - } - - public void completeHost(int index) { - completeHost(emittedHosts.get(index)); - } - - public void completeHost(Host host) { - ((PublishSubject)host.getCloseNotifier()).onCompleted(); - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/client/pool/FIFOIdleConnectionsHolderTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/client/pool/FIFOIdleConnectionsHolderTest.java deleted file mode 100644 index 0352d2c..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/client/pool/FIFOIdleConnectionsHolderTest.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.mockito.Mockito; -import rx.observers.TestSubscriber; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class FIFOIdleConnectionsHolderTest { - - @Rule - public final HolderRule holderRule = new HolderRule(); - - @Test(timeout = 60000) - public void testPoll() throws Exception { - holderRule.pollNow(); - - @SuppressWarnings("unchecked") - final PooledConnection mock = Mockito.mock(PooledConnection.class); - Mockito.when(mock.isUsable()).thenReturn(true); - - PooledConnection added = holderRule.addAConnection(); - - holderRule.pollNow(added); - - holderRule.pollNow(); // Poll removes the item. - } - - @Test(timeout = 60000) - public void testPeek() throws Exception { - holderRule.peekNow(); - - @SuppressWarnings("unchecked") - final PooledConnection mock = Mockito.mock(PooledConnection.class); - Mockito.when(mock.isUsable()).thenReturn(true); - - PooledConnection added = holderRule.addAConnection(); - - holderRule.peekNow(added); - - holderRule.peekNow(added); // Peek does not removes the item. - } - - @Test(timeout = 60000) - public void testAdd() throws Exception { - PooledConnection added = holderRule.addAConnection(); - - holderRule.peekNow(added); - - PooledConnection added2 = holderRule.addAConnection(); - - holderRule.peekNow(added, added2); // Get both items in the same order. - } - - @Test(timeout = 60000) - public void testRemove() throws Exception { - PooledConnection added = holderRule.addAConnection(); - PooledConnection added2 = holderRule.addAConnection(); - - holderRule.peekNow(added, added2); // Get both items in the same order. - - holderRule.holder.remove(added); - - holderRule.peekNow(added2); // one item is removed - } - - public static class HolderRule extends ExternalResource { - - private FIFOIdleConnectionsHolder holder; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - holder = new FIFOIdleConnectionsHolder<>(); - base.evaluate(); - } - }; - } - - @SafeVarargs - public final TestSubscriber> pollNow(PooledConnection... expected) { - TestSubscriber> subscriber = new TestSubscriber<>(); - holder.poll().subscribe(subscriber); - - subscriber.assertNoErrors(); - subscriber.assertTerminalEvent(); - - assertThat("Unexpected connections received from the holder.", subscriber.getOnNextEvents(), - hasSize(expected.length)); - - if (expected.length > 0) { - assertThat("Unexpected connections received from the holder.", subscriber.getOnNextEvents(), - contains(expected)); - } - - return subscriber; - } - - @SafeVarargs - public final TestSubscriber> peekNow(PooledConnection... expected) { - return peekNow(holder, expected); - } - - @SafeVarargs - public static TestSubscriber> peekNow( - IdleConnectionsHolder holder, - PooledConnection... expected) { - TestSubscriber> subscriber = new TestSubscriber<>(); - holder.peek().subscribe(subscriber); - - subscriber.assertNoErrors(); - subscriber.assertTerminalEvent(); - - assertThat("Unexpected connections received from the holder.", subscriber.getOnNextEvents(), - hasSize(expected.length)); - - if (expected.length > 0) { - assertThat("Unexpected connections received from the holder.", subscriber.getOnNextEvents(), - contains(expected)); - } - - return subscriber; - } - - public PooledConnection addAConnection() { - - @SuppressWarnings("unchecked") - PooledConnection mock = Mockito.mock(PooledConnection.class); - Mockito.when(mock.isUsable()).thenReturn(true); - - holder.add(mock); - - return mock; - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/client/pool/PoolLimitStrategyTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/client/pool/PoolLimitStrategyTest.java deleted file mode 100644 index e988f6f..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/client/pool/PoolLimitStrategyTest.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -import org.junit.Assert; -import org.junit.Test; - -import java.util.concurrent.TimeUnit; - -public class PoolLimitStrategyTest { - - @Test(timeout = 60000) - public void testMaxConnectionLimit() throws Exception { - - MaxConnectionsBasedStrategy strategy = new MaxConnectionsBasedStrategy(3); - long startTime = System.currentTimeMillis(); - Assert.assertTrue("Invalid permit acquire failure.", strategy.acquireCreationPermit(startTime, TimeUnit.MILLISECONDS)); - Assert.assertEquals("Unexpected available permits.", 2, strategy.getAvailablePermits()); - Assert.assertTrue("Invalid permit acquire failure.", strategy.acquireCreationPermit(startTime, TimeUnit.MILLISECONDS)); - Assert.assertEquals("Unexpected available permits.", 1, strategy.getAvailablePermits()); - Assert.assertTrue("Invalid permit acquire failure.", strategy.acquireCreationPermit(startTime, TimeUnit.MILLISECONDS)); - Assert.assertEquals("Unexpected available permits.", 0, strategy.getAvailablePermits()); - - Assert.assertFalse("Invalid permit acquire success.", strategy.acquireCreationPermit(startTime, TimeUnit.MILLISECONDS)); - - strategy.releasePermit(); - - Assert.assertEquals("Unexpected available permits.", 1, strategy.getAvailablePermits()); - Assert.assertTrue("Permit not available after release.", strategy.acquireCreationPermit(startTime, TimeUnit.MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testCompositeStrategy() throws Exception { - long startTime = System.currentTimeMillis(); - MaxConnectionsBasedStrategy global = new MaxConnectionsBasedStrategy(1); - MaxConnectionsBasedStrategy local = new MaxConnectionsBasedStrategy(2); - CompositePoolLimitDeterminationStrategy strategy = - new CompositePoolLimitDeterminationStrategy(local, global); - - Assert.assertTrue("Invalid permit acquire failure.", strategy.acquireCreationPermit(startTime, TimeUnit.MILLISECONDS)); - Assert.assertEquals("Unexpected available global permits.", 0, global.getAvailablePermits()); - Assert.assertEquals("Unexpected available local permits.", 1, local.getAvailablePermits()); - Assert.assertEquals("Unexpected available composite permits.", 0, strategy.getAvailablePermits()); // Should be min. of all strategies - - Assert.assertFalse("Invalid permit acquire success.", strategy.acquireCreationPermit(startTime, TimeUnit.MILLISECONDS)); - - Assert.assertEquals("Unexpected available global permits.", 0, global.getAvailablePermits()); - Assert.assertEquals("Unexpected available local permits.", 1, local.getAvailablePermits()); - Assert.assertEquals("Unexpected available composite permits.", 0, strategy.getAvailablePermits()); // Should be min. of all strategies - - strategy.releasePermit(); - - Assert.assertEquals("Unexpected available global permits.", 1, global.getAvailablePermits()); - Assert.assertEquals("Unexpected available local permits.", 2, local.getAvailablePermits()); - Assert.assertEquals("Unexpected available composite permits.", 1, strategy.getAvailablePermits()); // Should be min. of all strategies - - Assert.assertTrue("Invalid permit acquire failure.", strategy.acquireCreationPermit(startTime, TimeUnit.MILLISECONDS)); - Assert.assertEquals("Unexpected available global permits.", 0, global.getAvailablePermits()); - Assert.assertEquals("Unexpected available local permits.", 1, local.getAvailablePermits()); - Assert.assertEquals("Unexpected available composite permits.", 0, strategy.getAvailablePermits()); // Should be min. of all strategies - } - - @Test(timeout = 60000) - public void testFirstStrategyHasMorePermits() throws Exception { - long startTime = System.currentTimeMillis(); - MaxConnectionsBasedStrategy global = new MaxConnectionsBasedStrategy(2); - MaxConnectionsBasedStrategy local = new MaxConnectionsBasedStrategy(1); - CompositePoolLimitDeterminationStrategy strategy = new CompositePoolLimitDeterminationStrategy(local, global); - - Assert.assertTrue("Invalid permit acquire failure.", strategy.acquireCreationPermit(startTime, TimeUnit.MILLISECONDS)); - Assert.assertEquals("Unexpected available global permits.", 1, global.getAvailablePermits()); - Assert.assertEquals("Unexpected available local permits.", 0, local.getAvailablePermits()); - Assert.assertEquals("Unexpected available composite permits.", 0, strategy.getAvailablePermits()); // Should be min. of all strategies - - Assert.assertFalse("Invalid permit acquire success.", strategy.acquireCreationPermit(startTime, TimeUnit.MILLISECONDS)); - - strategy.releasePermit(); - - Assert.assertTrue("Invalid permit acquire failure.", strategy.acquireCreationPermit(startTime, TimeUnit.MILLISECONDS)); - Assert.assertEquals("Unexpected available global permits.", 1, global.getAvailablePermits()); - Assert.assertEquals("Unexpected available local permits.", 0, local.getAvailablePermits()); - Assert.assertEquals("Unexpected available composite permits.", 0, strategy.getAvailablePermits()); // Should be min. of all strategies - } - - @Test(timeout = 60000) - public void testIncrementDecrementMaxConnections() throws Exception { - long startTime = System.currentTimeMillis(); - MaxConnectionsBasedStrategy strategy = new MaxConnectionsBasedStrategy(1); - Assert.assertTrue("Invalid permit acquire failure.", strategy.acquireCreationPermit(startTime, TimeUnit.MILLISECONDS)); - Assert.assertEquals("Unexpected available permits.", 0, strategy.getAvailablePermits()); - - Assert.assertFalse("Invalid permit acquire success.", strategy.acquireCreationPermit(startTime, TimeUnit.MILLISECONDS)); - - strategy.incrementMaxConnections(1); - - Assert.assertEquals("Unexpected available permits.", 1, strategy.getAvailablePermits()); - Assert.assertTrue("Permit not available after release.", strategy.acquireCreationPermit(startTime, TimeUnit.MILLISECONDS)); - - strategy.releasePermit(); - strategy.decrementMaxConnections(1); - - Assert.assertEquals("Unexpected available permits.", 0, strategy.getAvailablePermits()); - Assert.assertFalse("Invalid permit acquire success.", strategy.acquireCreationPermit(startTime, TimeUnit.MILLISECONDS)); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/client/pool/PooledConnectionProviderImplTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/client/pool/PooledConnectionProviderImplTest.java deleted file mode 100644 index 0d7bab7..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/client/pool/PooledConnectionProviderImplTest.java +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.logging.LoggingHandler; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.ConnectionImpl; -import io.reactivex.netty.client.ClientConnectionToChannelBridge; -import io.reactivex.netty.client.ConnectionProvider; -import io.reactivex.netty.client.Host; -import io.reactivex.netty.client.HostConnector; -import io.reactivex.netty.client.events.ClientEventListener; -import io.reactivex.netty.events.EventAttributeKeys; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.events.EventSource; -import io.reactivex.netty.test.util.MockEventPublisher; -import io.reactivex.netty.test.util.TrackableMetricEventsListener; -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.Observable; -import rx.Observable.OnSubscribe; -import rx.Subscriber; -import rx.observers.TestSubscriber; -import rx.schedulers.Schedulers; -import rx.schedulers.TestScheduler; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.net.InetSocketAddress; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import static io.reactivex.netty.client.pool.MaxConnectionsBasedStrategy.*; -import static java.lang.annotation.ElementType.*; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class PooledConnectionProviderImplTest { - - @Rule - public ExpectedException thrown= ExpectedException.none(); - - @Rule - public final PooledFactoryRule pooledFactoryRule = new PooledFactoryRule(); - - @Test(timeout = 6000000) - public void testConnect() throws Exception { - pooledFactoryRule.getAConnection(); - pooledFactoryRule.assertNoIdleConnection(); - } - - @MaxConnections(1) - @Test(timeout = 60000) - public void testReuse() throws Exception { - final PooledConnection conn1 = pooledFactoryRule.getAConnection(); - - pooledFactoryRule.returnToIdle(conn1); - - PooledConnection conn2 = pooledFactoryRule.getAConnection(); - - assertThat("Connection not reused.", conn2, is(conn1)); - } - - @Test(timeout = 60000) - public void testRelease() throws Exception { - _testRelease(); - } - - @Test(timeout = 60000) - public void testDiscard() throws Exception { - final Connection connection = pooledFactoryRule.getAConnection(); - assertThat("Connection is null.", connection, notNullValue()); - - pooledFactoryRule.assertNoIdleConnection(); - - /*This attribute will discard on close*/ - connection.unsafeNettyChannel().attr(ClientConnectionToChannelBridge.DISCARD_CONNECTION).set(true); - - /* Close will discard */ - pooledFactoryRule.closeAndAwait(connection); /*Throw error or close quietly*/ - - pooledFactoryRule.assertNoIdleConnection(); - } - - @Test(timeout = 60000) - public void testExpired() throws Exception { - final PooledConnection connection = pooledFactoryRule.getAConnection(); - assertThat("Connection is null.", connection, notNullValue()); - - pooledFactoryRule.assertNoIdleConnection(); - - /*This attribute will discard on close*/ - connection.setLastReturnToPoolTimeMillis(System.currentTimeMillis() - 30000); - - /* Close will discard */ - pooledFactoryRule.closeAndAwait(connection); /*Throw error or close quietly*/ - - pooledFactoryRule.assertNoIdleConnection(); - } - - @Test(timeout = 60000000) - public void testIdleConnectionCleanup() throws Exception { - PooledConnection idleConnection = _testRelease(); - - /*Force discard by next idle connection reap*/ - idleConnection.unsafeNettyChannel().attr(ClientConnectionToChannelBridge.DISCARD_CONNECTION).set(true); - - pooledFactoryRule.testScheduler.advanceTimeBy(1, TimeUnit.MINUTES); - - pooledFactoryRule.assertNoIdleConnection(); - } - - @MaxConnections(1) - @Test(timeout = 60000) - public void testPoolExhaustion() throws Exception { - thrown.expectCause(isA(PoolExhaustedException.class)); - - pooledFactoryRule.getAConnection(); - - pooledFactoryRule.getProvider().newConnectionRequest().toBlocking().single(); - } - - @Test(timeout = 60000) - public void testConnectFailed() throws Exception { - PooledConnectionProvider factory; - PoolConfig config = new PoolConfig<>(); - config.idleConnectionsHolder(pooledFactoryRule.holder); - - MockEventPublisher publisher = MockEventPublisher.disabled(); - ClientEventListener listener = new ClientEventListener(); - EmbeddedConnectionProvider connectionProvider = new EmbeddedConnectionProvider(publisher, true, listener); - Host host = new Host(new InetSocketAddress("127.0.0.1", 0)); - HostConnector connector = new HostConnector<>(host, connectionProvider, - publisher, publisher, listener); - - factory = new PooledConnectionProviderImpl<>(config, connector); - - TestSubscriber subscriber = new TestSubscriber<>(); - factory.newConnectionRequest().subscribe(subscriber); - - subscriber.assertTerminalEvent(); - - assertThat("Error not returned to connect.", subscriber.getOnCompletedEvents(), is(empty())); - assertThat("Error not returned to connect.", subscriber.getOnNextEvents(), is(empty())); - - } - - @Test(timeout = 60000) - public void testMetricEventCallback() throws Throwable { - TrackableMetricEventsListener eventsListener = new TrackableMetricEventsListener(); - - pooledFactoryRule.init(DEFAULT_MAX_CONNECTIONS, MockEventPublisher.enabled(), - MockEventPublisher.enabled(), eventsListener); - final PooledConnection connection = pooledFactoryRule.getAConnection(); - - assertThat("Unexpected acquire attempted count.", eventsListener.getAcquireAttemptedCount(), - is(1L)); - assertThat("Unexpected acquire succedded count.", eventsListener.getAcquireSucceededCount(), - is(1L)); - assertThat("Unexpected acquire failed count.", eventsListener.getAcquireFailedCount(), - is(0L)); - - pooledFactoryRule.returnToIdle(connection); - - assertThat("Unexpected release attempted count.", eventsListener.getReleaseAttemptedCount(), - is(1L)); - assertThat("Unexpected release succeeded count.", eventsListener.getReleaseSucceededCount(), - is(1L)); - assertThat("Unexpected release failed count.", eventsListener.getReleaseFailedCount(), is(0L)); - - final PooledConnection reusedConn = pooledFactoryRule.getAConnection(); - - Assert.assertEquals("Reused connection not same as original.", connection, reusedConn); - - assertThat("Unexpected acquire attempted count.", eventsListener.getAcquireAttemptedCount(), - is(2L)); - assertThat("Unexpected acquire succedded count.", eventsListener.getAcquireSucceededCount(), - is(2L)); - assertThat("Unexpected acquire failed count.", eventsListener.getAcquireFailedCount(), - is(0L)); - assertThat("Unexpected reuse count.", eventsListener.getReuseCount(), is(1L)); - - pooledFactoryRule.closeAndAwait(reusedConn); - - assertThat("Unexpected release attempted count.", eventsListener.getReleaseAttemptedCount(), is(2L)); - assertThat("Unexpected release succeeded count.", eventsListener.getReleaseSucceededCount(), is(2L)); - assertThat("Unexpected release failed count.", eventsListener.getReleaseFailedCount(), is(0L)); - - pooledFactoryRule.provider.discard(reusedConn).toBlocking().lastOrDefault(null); - - assertThat("Unexpected release attempted count.", eventsListener.getReleaseAttemptedCount(), is(2L)); - assertThat("Unexpected release succeeded count.", eventsListener.getReleaseSucceededCount(), is(2L)); - assertThat("Unexpected release failed count.", eventsListener.getReleaseFailedCount(), is(0L)); - assertThat("Unexpected connection eviction count.", eventsListener.getEvictionCount(), is(2L)); - } - - private PooledConnection _testRelease() throws Exception { - final Connection connection = pooledFactoryRule.getAConnection(); - - pooledFactoryRule.assertNoIdleConnection(); - - /* Close will release */ - pooledFactoryRule.closeAndAwait(connection); /*Throw error or close quietly*/ - - PooledConnection connIdle = - pooledFactoryRule.holder.peek().defaultIfEmpty(null).toBlocking().single(); - - assertThat("Release did not add to idle.", connIdle, not(nullValue())); - - return connIdle; - } - - public static class PooledFactoryRule extends ExternalResource { - - private PooledConnectionProvider provider; - private TestScheduler testScheduler; - private FIFOIdleConnectionsHolder holder; - - @Override - public Statement apply(final Statement base, final Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - MaxConnections maxConnections1 = description.getAnnotation(MaxConnections.class); - ClientEventListener listener = new ClientEventListener(); - final MockEventPublisher publisher = MockEventPublisher.disabled(); - int maxConnections = null == maxConnections1? DEFAULT_MAX_CONNECTIONS - : maxConnections1.value(); - init(maxConnections, publisher, publisher, listener); - base.evaluate(); - } - }; - } - - protected void init(int maxConnections, EventSource eventSource, - EventPublisher publisher, ClientEventListener clientListener) { - testScheduler = Schedulers.test(); - Observable idleConnCleaner = Observable.timer(1, TimeUnit.MINUTES, testScheduler); - holder = new FIFOIdleConnectionsHolder<>(); - PoolConfig config = new PoolConfig<>(); - config.idleConnectionsCleanupTimer(idleConnCleaner) - .maxConnections(maxConnections) - .idleConnectionsHolder(holder); - Host host = new Host(new InetSocketAddress("127.0.0.1", 0)); - ConnectionProvider cp = new EmbeddedConnectionProvider(publisher, clientListener); - HostConnector connector = new HostConnector<>(host, cp, eventSource, publisher, - clientListener); - provider = new PooledConnectionProviderImpl<>(config, connector); - } - - public PooledConnectionProvider getProvider() { - return provider; - } - - public PooledConnection getAConnection(Observable> connectionObservable) - throws InterruptedException, ExecutionException, TimeoutException { - TestSubscriber> connSub = new TestSubscriber<>(); - connectionObservable.subscribe(connSub); - - connSub.awaitTerminalEvent(); - connSub.assertNoErrors(); - assertThat("Unexpected connections returned on connect.", connSub.getOnNextEvents(), hasSize(1)); - - Connection connection = connSub.getOnNextEvents().get(0); - assertThat("Connection is null.", connection, notNullValue()); - - return (PooledConnection) connection; - } - - public PooledConnection getAConnection() - throws InterruptedException, ExecutionException, TimeoutException { - return getAConnection(getProvider().newConnectionRequest()); - } - - public void closeAndAwait(Connection toClose) throws Exception { - EmbeddedChannel embeddedChannel= (EmbeddedChannel) toClose.unsafeNettyChannel(); - - final TestSubscriber testSubscriber = new TestSubscriber<>(); - - toClose.close().subscribe(testSubscriber); - - embeddedChannel.runPendingTasks(); - - testSubscriber.awaitTerminalEvent(); - - testSubscriber.assertNoErrors(); - } - - public void assertNoIdleConnection() { - final TestSubscriber> subscriber = new TestSubscriber<>(); - holder.peek().subscribe(subscriber); - - subscriber.awaitTerminalEvent(); - subscriber.assertNoErrors(); - - assertThat("Idle connection available", subscriber.getOnNextEvents(), is(empty())); - } - - public void returnToIdle(PooledConnection conn1) { - conn1.closeNow(); - EmbeddedChannel embeddedChannel= (EmbeddedChannel) conn1.unsafeNettyChannel(); - embeddedChannel.runPendingTasks(); - - TestSubscriber> subscriber = new TestSubscriber<>(); - holder.peek().take(1).subscribe(subscriber); - subscriber.awaitTerminalEvent(); - - assertThat("Unexpected number of idle connections post release.", subscriber.getOnNextEvents(), hasSize(1)); - assertThat("Connection not returned to idle holder on close.", - subscriber.getOnNextEvents().get(0), is(conn1)); - } - - } - - private static class EmbeddedConnectionProvider implements ConnectionProvider { - - private final EventPublisher publisher; - private final boolean failConnect; - private final ClientEventListener clientListener; - - public EmbeddedConnectionProvider(EventPublisher publisher, boolean failConnect, - ClientEventListener clientListener) { - this.publisher = publisher; - this.failConnect = failConnect; - this.clientListener = clientListener; - } - - public EmbeddedConnectionProvider(EventPublisher publisher, ClientEventListener clientListener) { - this(publisher, false, clientListener); - } - - @Override - public Observable> newConnectionRequest() { - if (failConnect) { - return Observable.error(new IllegalStateException("Deliberate connect failure")); - } - - return Observable.create(new OnSubscribe>() { - @Override - public void call(Subscriber> s) { - EmbeddedChannel c = new EmbeddedChannel(new LoggingHandler()); - c.attr(EventAttributeKeys.EVENT_PUBLISHER).set(publisher); - if (publisher.publishingEnabled()) { - c.attr(EventAttributeKeys.CLIENT_EVENT_LISTENER).set(clientListener); - c.attr(EventAttributeKeys.CONNECTION_EVENT_LISTENER).set(clientListener); - } - ClientConnectionToChannelBridge.addToPipeline(c.pipeline(), false); - s.onNext(ConnectionImpl.fromChannel(c)); - s.onCompleted(); - } - }); - } - } - - @Retention(RetentionPolicy.RUNTIME) - @Target(METHOD) - public @interface MaxConnections { - int value() default DEFAULT_MAX_CONNECTIONS; - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/client/pool/PreferCurrentEventLoopHolderTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/client/pool/PreferCurrentEventLoopHolderTest.java deleted file mode 100644 index 348e7b5..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/client/pool/PreferCurrentEventLoopHolderTest.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.client.pool; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.util.concurrent.Future; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.ConnectionImpl; -import io.reactivex.netty.client.pool.PooledConnection.Owner; -import io.reactivex.netty.client.pool.PreferCurrentEventLoopHolder.IdleConnectionsHolderFactory; -import io.reactivex.netty.events.EventAttributeKeys; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.test.util.MockEventPublisher; -import io.reactivex.netty.threads.PreferCurrentEventLoopGroup; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.Observable; -import rx.schedulers.Schedulers; - -import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class PreferCurrentEventLoopHolderTest { - - @Rule - public final PreferCurrentELHolderRule preferCurrentELHolderRule = new PreferCurrentELHolderRule(); - - @Test(timeout = 60000) - public void testPollOutOfEventloop() throws Exception { - PooledConnection connection1 = preferCurrentELHolderRule.addConnection(); - - - PooledConnection connection = preferCurrentELHolderRule.holder.poll() - .defaultIfEmpty(null).toBlocking() - .single(); - - assertThat("Unexpected connection.", connection, is(connection1)); - } - - @Test(timeout = 60000) - public void testPollInEventloop() throws Exception { - final PooledConnection connection1 = preferCurrentELHolderRule.addConnection(); - - preferCurrentELHolderRule.runFromChannelEventLoop(new Callable() { - @Override - public Void call() throws Exception { - PooledConnection connection = preferCurrentELHolderRule.holder.poll() - .defaultIfEmpty(null) - .toBlocking() - .single(); - assertThat("Connection available in the eventloop.", connection, is(connection1)); - return null; - } - }).get(1, TimeUnit.MINUTES); - } - - @Test - public void testPollThisEventLoopConnectionsInEl() throws Exception { - final PooledConnection connection1 = preferCurrentELHolderRule.addConnection(); - preferCurrentELHolderRule.runFromChannelEventLoop(new Callable() { - @Override - public Void call() throws Exception { - PooledConnection connection = - preferCurrentELHolderRule.holder.pollThisEventLoopConnections() - .defaultIfEmpty(null) - .toBlocking().single(); - assertThat("Connection available in the eventloop.", connection, is(connection1)); - return null; - } - }).get(1, TimeUnit.MINUTES); - } - - @Test(timeout = 60000) - public void testPollRemovesItem() throws Exception { - PooledConnection connection1 = preferCurrentELHolderRule.addConnection(); - PooledConnection connection = preferCurrentELHolderRule.holder.poll() - .defaultIfEmpty(null) - .toBlocking().single(); - - assertThat("Connection not available with poll.", connection, is(connection1)); - - connection = preferCurrentELHolderRule.holder.poll() - .defaultIfEmpty(null) - .toBlocking().single(); - - assertThat("Connection available after poll.", connection, is(nullValue())); - } - - @Test(timeout = 60000) - public void testPeek() throws Exception { - PooledConnection connection1 = preferCurrentELHolderRule.addConnection(); - PooledConnection connection = preferCurrentELHolderRule.holder.peek() - .defaultIfEmpty(null) - .toBlocking().single(); - - assertThat("Connection not available with peek.", connection, is(connection1)); - - connection = preferCurrentELHolderRule.holder.peek().defaultIfEmpty(null) - .toBlocking().single(); - - assertThat("Connection not available after peek.", connection, not(nullValue())); - assertThat("Unexpected connection on peek.", connection, is(connection1)); - } - - @Test(timeout = 60000) - public void testRemove() throws Exception { - PooledConnection connection1 = preferCurrentELHolderRule.addConnection(); - PooledConnection connection = preferCurrentELHolderRule.holder.peek() - .defaultIfEmpty(null) - .toBlocking().single(); - - assertThat("Connection not available with peek.", connection, is(connection1)); - - preferCurrentELHolderRule.holder.remove(connection1); - - connection = preferCurrentELHolderRule.holder.peek().defaultIfEmpty(null).toBlocking().single(); - - assertThat("Connection not removed.", connection, is(nullValue())); - } - - public static class PreferCurrentELHolderRule extends ExternalResource implements Owner { - - private PreferCurrentEventLoopHolder holder; - private EventPublisher eventPublisher; - private PoolConfig poolConfig; - private ConcurrentLinkedQueue> discarded; - private ConcurrentLinkedQueue> released; - private ExecutorService eventLoopThread; - private EmbeddedChannel channel; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - eventLoopThread = Executors.newFixedThreadPool(1); - channel = new EmbeddedChannel(new LoggingHandler()); - PreferCurrentEventLoopGroup eventLoopGroup = new PreferCurrentEventLoopGroup(channel.eventLoop()); - eventPublisher = MockEventPublisher.disabled(); - channel.attr(EventAttributeKeys.EVENT_PUBLISHER).set(eventPublisher); - holder = new PreferCurrentEventLoopHolder<>(eventLoopGroup, - new IdleConnectionsHolderFactoryImpl()); - poolConfig = new PoolConfig<>(); - poolConfig.maxIdleTimeoutMillis(TimeUnit.DAYS.toMillis(1)) - .idleConnectionsCleanupTimer(Observable.timer(1, TimeUnit.DAYS, Schedulers.test())) - .limitDeterminationStrategy(new MaxConnectionsBasedStrategy(1)) - .idleConnectionsHolder(holder); - discarded = new ConcurrentLinkedQueue<>(); - released = new ConcurrentLinkedQueue<>(); - base.evaluate(); - } - }; - } - - public PooledConnection addConnection() throws Exception { - Connection connection = ConnectionImpl.fromChannel(channel); - PooledConnection pooledConnection = PooledConnection.create(this, - poolConfig.getMaxIdleTimeMillis(), - connection); - holder.add(pooledConnection); - - runAllPendingTasksOnChannel(); - - return pooledConnection; - } - - @Override - public Observable release(PooledConnection connection) { - released.add(connection); - return Observable.empty(); - } - - @Override - public Observable discard(PooledConnection connection) { - discarded.add(connection); - return Observable.empty(); - } - - public Future runFromChannelEventLoop(Callable runnable) throws Exception { - Future toReturn = channel.eventLoop().submit(runnable); - runAllPendingTasksOnChannel(); - return toReturn; - } - - public void runAllPendingTasksOnChannel() throws Exception { - eventLoopThread.submit(new Runnable() { - @Override - public void run() { - channel.runPendingTasks(); - } - }).get(1, TimeUnit.MINUTES); - } - - private static class IdleConnectionsHolderFactoryImpl implements IdleConnectionsHolderFactory { - - @Override - public IdleConnectionsHolder call() { - return new FIFOIdleConnectionsHolder<>(); - } - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/events/ListenersHolderRule.java b/netty-http-rx/src/test/java/io/reactivex/netty/events/ListenersHolderRule.java deleted file mode 100644 index 4286a00..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/events/ListenersHolderRule.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.events; - -import io.reactivex.netty.test.util.MockEventListener; -import org.hamcrest.Matchers; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.Subscription; - -import java.util.Collection; - -import static org.hamcrest.MatcherAssert.*; - -public class ListenersHolderRule extends ExternalResource { - - private ListenersHolder holder; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - holder = new ListenersHolder<>(); - base.evaluate(); - } - }; - } - - public ListenerWithSub addAListener() { - final MockEventListener listener = new MockEventListener(); - Subscription subscription = holder.subscribe(listener); - assertListenerAdded(listener); - - return new ListenerWithSub(listener, subscription); - } - - public void assertListenerAdded(MockEventListener... listeners) { - Collection allListeners = holder.getAllListeners(); - assertThat("Unexpected listeners count in the holder.", allListeners, Matchers.hasSize(listeners.length)); - assertThat("Listener not added to the holder.", allListeners, Matchers.contains(listeners)); - } - - public ListenersHolder getHolder() { - return holder; - } - - public static class ListenerWithSub { - - final MockEventListener listener; - final Subscription subscription; - - public ListenerWithSub(MockEventListener listener, Subscription subscription) { - this.subscription = subscription; - this.listener = listener; - } - - public MockEventListener getListener() { - return listener; - } - - public Subscription getSubscription() { - return subscription; - } - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/events/ListenersHolderTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/events/ListenersHolderTest.java deleted file mode 100644 index 9cc697b..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/events/ListenersHolderTest.java +++ /dev/null @@ -1,564 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.events; - -import io.reactivex.netty.events.ListenersHolderRule.ListenerWithSub; -import io.reactivex.netty.test.util.MockEventListener; -import org.junit.Rule; -import org.junit.Test; -import rx.Subscription; -import rx.functions.Action1; -import rx.functions.Action2; -import rx.functions.Action3; -import rx.functions.Action4; -import rx.functions.Action5; - -import java.util.Collection; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; - -public class ListenersHolderTest { - - @Rule - public final ListenersHolderRule holderRule = new ListenersHolderRule(); - - @Test(timeout = 60000) - public void testSubscribe() throws Exception { - - ListenerWithSub l = holderRule.addAListener(); - - holderRule.assertListenerAdded(l.listener); - - l.subscription.unsubscribe(); - - assertThat("Listener not removed on unsubscribe.", holderRule.getHolder().getAllListeners(), is(empty())); - } - - @Test(timeout = 60000) - public void testMultipleListeners() throws Exception { - final MockEventListener listener1 = new MockEventListener(); - final MockEventListener listener2 = new MockEventListener(); - Subscription subscription1 = holderRule.getHolder().subscribe(listener1); - Subscription subscription2 = holderRule.getHolder().subscribe(listener2); - - holderRule.assertListenerAdded(listener1, listener2); - - subscription1.unsubscribe(); - - assertThat("Listener not removed on unsubscribe.", holderRule.getHolder().getAllListeners(), hasSize(1)); - assertThat("Listener not removed on unsubscribe.", holderRule.getHolder().getAllListeners(), - not(contains(listener1))); - assertThat("Listener not removed on unsubscribe.", holderRule.getHolder().getAllListeners(), - contains(listener2)); - - subscription2.unsubscribe(); - - assertThat("Listener not removed on unsubscribe.", holderRule.getHolder().getAllListeners(), is(empty())); - } - - @Test(timeout = 60000) - public void testPublishingEnabled() throws Exception { - - assertThat("Publishing enabled with no listeners", holderRule.getHolder().publishingEnabled(), is(false)); - - ListenerWithSub l1 = holderRule.addAListener(); - - assertThat("Publishing disabled with a listener", holderRule.getHolder().publishingEnabled(), is(true)); - - l1.subscription.unsubscribe(); - - assertThat("Listener not removed on unsubscribe.", holderRule.getHolder().getAllListeners(), is(empty())); - - assertThat("Publishing enabled post listener unsubscribe", holderRule.getHolder().publishingEnabled(), is(false)); - } - - @Test(timeout = 60000) - public void testDispose() throws Exception { - ListenerWithSub l = holderRule.addAListener(); - holderRule.getHolder().dispose(); - - assertThat("On complete not called on dispose.", l.listener.getOnCompletedCount(), is(1)); - assertThat("Listener not unsubscribed on dispose.", l.subscription.isUnsubscribed(), is(true)); - - assertThat("Listener not removed on dispose.", holderRule.getHolder().getAllListeners(), - not(contains(l.listener))); - } - - @Test(timeout = 60000) - public void testDisposeWithExceptions() throws Exception { - final MockEventListener listener1 = new MockEventListener(true); - final MockEventListener listener2 = new MockEventListener(); - Subscription subscription1 = holderRule.getHolder().subscribe(listener1); - Subscription subscription2 = holderRule.getHolder().subscribe(listener2); - - assertThat("Listeners not added.", holderRule.getHolder().getAllListeners(), hasSize(2)); - assertThat("Listeners not added.", holderRule.getHolder().getAllListeners(), contains(listener1, listener2)); - - try { - holderRule.getHolder().dispose(); - throw new AssertionError("Error not thrown on dispose."); - } catch (Exception e) { - // Expected. - } - - assertThat("First listener not completed.", listener1.getOnCompletedCount(), is(1)); - assertThat("Second listener not completed.", listener2.getOnCompletedCount(), is(1)); - - assertThat("First listener not unsubscribed.", subscription1.isUnsubscribed(), is(true)); - assertThat("Second listener not unsubscribed.", subscription2.isUnsubscribed(), is(true)); - - assertThat("Listeners not removed post dispose.", holderRule.getHolder().getAllListeners(), is(empty())); - } - - @Test(timeout = 60000) - public void testInvokeListeners() throws Exception { - final MockEventListener listener = new MockEventListener(); - holderRule.getHolder().subscribe(listener); - holderRule.assertListenerAdded(listener); - - holderRule.getHolder().invokeListeners(new Action1() { - @Override - public void call(MockEventListener mockEventListener) { - mockEventListener.anEvent(); - } - }); - - assertThat("Listener not invoked.", listener.getEventInvocationCount(), is(1)); - } - - @Test(timeout = 60000) - public void testInvokeListenersMulti() throws Exception { - final MockEventListener listener1 = new MockEventListener(); - holderRule.getHolder().subscribe(listener1); - - final MockEventListener listener2 = new MockEventListener(); - holderRule.getHolder().subscribe(listener2); - holderRule.assertListenerAdded(listener1, listener2); - - holderRule.getHolder().invokeListeners(new Action1() { - @Override - public void call(MockEventListener mockEventListener) { - mockEventListener.anEvent(); - } - }); - - assertThat("Listener not invoked.", listener1.getEventInvocationCount(), is(1)); - assertThat("Listener not invoked.", listener2.getEventInvocationCount(), is(1)); - } - - @Test(timeout = 60000) - public void testInvokeListenersRaiseException() throws Exception { - final MockEventListener listener1 = new MockEventListener(true); - final MockEventListener listener2 = new MockEventListener(); - holderRule.getHolder().subscribe(listener1); - holderRule.getHolder().subscribe(listener2); - - assertThat("Listeners not added.", holderRule.getHolder().getAllListeners(), hasSize(2)); - assertThat("Listeners not added.", holderRule.getHolder().getAllListeners(), contains(listener1, listener2)); - - holderRule.getHolder().invokeListeners(new Action1() { - @Override - public void call(MockEventListener mockEventListener) { - mockEventListener.anEvent(); - } - }); - - assertThat("First listener not invoked.", listener1.getEventInvocationCount(), is(1)); - assertThat("Second listener not invoked.", listener2.getEventInvocationCount(), is(1)); - } - - @Test(timeout = 60000) - public void testInvokeListenersWithDuration() throws Exception { - final MockEventListener listener = new MockEventListener(); - holderRule.getHolder().subscribe(listener); - holderRule.assertListenerAdded(listener); - - holderRule.getHolder().invokeListeners(new Action3() { - @Override - public void call(MockEventListener mockEventListener, Long duration, TimeUnit timeUnit) { - mockEventListener.anEventWithDuration(duration, timeUnit); - } - }, 1, TimeUnit.MICROSECONDS); - - assertThat("Listener not invoked.", listener.getEventInvocationCount(), is(1)); - assertThat("Listener not invoked with duration.", listener.getDuration(), is(1L)); - assertThat("Listener not invoked with time unit.", listener.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - } - - @Test(timeout = 60000) - public void testInvokeListenersMultiWithDuration() throws Exception { - final MockEventListener listener1 = new MockEventListener(); - holderRule.getHolder().subscribe(listener1); - - final MockEventListener listener2 = new MockEventListener(); - holderRule.getHolder().subscribe(listener2); - holderRule.assertListenerAdded(listener1, listener2); - - holderRule.getHolder().invokeListeners(new Action3() { - @Override - public void call(MockEventListener mockEventListener, Long duration, TimeUnit timeUnit) { - mockEventListener.anEventWithDuration(duration, timeUnit); - } - }, 1, TimeUnit.MICROSECONDS); - - - assertThat("Listener not invoked.", listener1.getEventInvocationCount(), is(1)); - assertThat("Listener not invoked with duration.", listener1.getDuration(), is(1L)); - assertThat("Listener not invoked with time unit.", listener1.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - - assertThat("Second listener not invoked.", listener2.getEventInvocationCount(), is(1)); - assertThat("Second listener not invoked with duration.", listener2.getDuration(), is(1L)); - assertThat("Second listener not invoked with time unit.", listener2.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - } - - @Test(timeout = 60000) - public void testInvokeListenersWithDurationRaiseException() throws Exception { - final MockEventListener listener1 = new MockEventListener(true); - holderRule.getHolder().subscribe(listener1); - - final MockEventListener listener2 = new MockEventListener(); - holderRule.getHolder().subscribe(listener2); - holderRule.assertListenerAdded(listener1, listener2); - - holderRule.getHolder().invokeListeners(new Action3() { - @Override - public void call(MockEventListener mockEventListener, Long duration, TimeUnit timeUnit) { - mockEventListener.anEventWithDuration(duration, timeUnit); - } - }, 1, TimeUnit.MICROSECONDS); - - - assertThat("Listener not invoked.", listener1.getEventInvocationCount(), is(1)); - assertThat("Listener not invoked with duration.", listener1.getDuration(), is(1L)); - assertThat("Listener not invoked with time unit.", listener1.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - - assertThat("Second listener not invoked.", listener2.getEventInvocationCount(), is(1)); - assertThat("Second listener not invoked with duration.", listener2.getDuration(), is(1L)); - assertThat("Second listener not invoked with time unit.", listener2.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - } - - @Test(timeout = 60000) - public void testInvokeListenersWithDurationAndError() throws Exception { - final MockEventListener listener = new MockEventListener(); - holderRule.getHolder().subscribe(listener); - holderRule.assertListenerAdded(listener); - - final Throwable expected = new NullPointerException(); - holderRule.getHolder().invokeListeners(new Action4() { - @Override - public void call(MockEventListener mockEventListener, Long duration, TimeUnit timeUnit, Throwable t) { - mockEventListener.anEventWithDurationAndError(duration, timeUnit, t); - } - }, 1, TimeUnit.MICROSECONDS, expected); - - assertThat("Listener not invoked.", listener.getEventInvocationCount(), is(1)); - assertThat("Listener not invoked with duration.", listener.getDuration(), is(1L)); - assertThat("Listener not invoked with time unit.", listener.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - assertThat("Listener not invoked with error.", listener.getRecievedError(), is(expected)); - } - - @Test(timeout = 60000) - public void testInvokeListenersMultiWithDurationAndError() throws Exception { - final MockEventListener listener1 = new MockEventListener(); - holderRule.getHolder().subscribe(listener1); - - final MockEventListener listener2 = new MockEventListener(); - holderRule.getHolder().subscribe(listener2); - holderRule.assertListenerAdded(listener1, listener2); - - final Throwable expected = new NullPointerException(); - holderRule.getHolder().invokeListeners(new Action4() { - @Override - public void call(MockEventListener mockEventListener, Long duration, TimeUnit timeUnit, Throwable t) { - mockEventListener.anEventWithDurationAndError(duration, timeUnit, t); - } - }, 1, TimeUnit.MICROSECONDS, expected); - - - assertThat("Listener not invoked.", listener1.getEventInvocationCount(), is(1)); - assertThat("Listener not invoked with duration.", listener1.getDuration(), is(1L)); - assertThat("Listener not invoked with time unit.", listener1.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - assertThat("Listener not invoked with error.", listener1.getRecievedError(), is(expected)); - - assertThat("Second listener not invoked.", listener2.getEventInvocationCount(), is(1)); - assertThat("Second listener not invoked with duration.", listener2.getDuration(), is(1L)); - assertThat("Second listener not invoked with time unit.", listener2.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - assertThat("Second listener not invoked with error.", listener2.getRecievedError(), is(expected)); - } - - @Test(timeout = 60000) - public void testInvokeListenersWithDurationAndErrorRaiseException() throws Exception { - final MockEventListener listener1 = new MockEventListener(true); - holderRule.getHolder().subscribe(listener1); - - final MockEventListener listener2 = new MockEventListener(); - holderRule.getHolder().subscribe(listener2); - holderRule.assertListenerAdded(listener1, listener2); - - final Throwable expected = new NullPointerException(); - holderRule.getHolder().invokeListeners(new Action4() { - @Override - public void call(MockEventListener mockEventListener, Long duration, TimeUnit timeUnit, Throwable t) { - mockEventListener.anEventWithDurationAndError(duration, timeUnit, t); - } - }, 1, TimeUnit.MICROSECONDS, expected); - - - assertThat("Listener not invoked.", listener1.getEventInvocationCount(), is(1)); - assertThat("Listener not invoked with duration.", listener1.getDuration(), is(1L)); - assertThat("Listener not invoked with time unit.", listener1.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - assertThat("Listener not invoked with error.", listener1.getRecievedError(), is(expected)); - - assertThat("Second listener not invoked.", listener2.getEventInvocationCount(), is(1)); - assertThat("Second listener not invoked with duration.", listener2.getDuration(), is(1L)); - assertThat("Second listener not invoked with time unit.", listener2.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - assertThat("Second listener not invoked with error.", listener2.getRecievedError(), is(expected)); - } - - @Test(timeout = 60000) - public void testInvokeListenersWithDurationAndArg() throws Exception { - final MockEventListener listener = new MockEventListener(); - holderRule.getHolder().subscribe(listener); - holderRule.assertListenerAdded(listener); - - final String arg = "doom"; - holderRule.getHolder().invokeListeners(new Action4() { - @Override - public void call(MockEventListener mockEventListener, Long duration, TimeUnit timeUnit, String arg) { - mockEventListener.anEventWithDurationAndArg(duration, timeUnit, arg); - } - }, 1, TimeUnit.MICROSECONDS, arg); - - assertThat("Listener not invoked.", listener.getEventInvocationCount(), is(1)); - assertThat("Listener not invoked with duration.", listener.getDuration(), is(1L)); - assertThat("Listener not invoked with time unit.", listener.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - assertThat("Listener not invoked with argument.", listener.getArg(), is(arg)); - } - - @Test(timeout = 60000) - public void testInvokeListenersMultiWithDurationAndArg() throws Exception { - final MockEventListener listener1 = new MockEventListener(); - holderRule.getHolder().subscribe(listener1); - - final MockEventListener listener2 = new MockEventListener(); - holderRule.getHolder().subscribe(listener2); - holderRule.assertListenerAdded(listener1, listener2); - - final String arg = "doom"; - holderRule.getHolder().invokeListeners(new Action4() { - @Override - public void call(MockEventListener mockEventListener, Long duration, TimeUnit timeUnit, String arg) { - mockEventListener.anEventWithDurationAndArg(duration, timeUnit, arg); - } - }, 1, TimeUnit.MICROSECONDS, arg); - - assertThat("Listener not invoked.", listener1.getEventInvocationCount(), is(1)); - assertThat("Listener not invoked with duration.", listener1.getDuration(), is(1L)); - assertThat("Listener not invoked with time unit.", listener1.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - assertThat("Listener not invoked with argument.", listener1.getArg(), is(arg)); - - assertThat("Second listener not invoked.", listener2.getEventInvocationCount(), is(1)); - assertThat("Second listener not invoked with duration.", listener2.getDuration(), is(1L)); - assertThat("Second listener not invoked with time unit.", listener2.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - assertThat("Second listener not invoked with argument.", listener2.getArg(), is(arg)); - } - - @Test(timeout = 60000) - public void testInvokeListenersMultiWithDurationErrorAndArg() throws Exception { - final MockEventListener listener1 = new MockEventListener(); - holderRule.getHolder().subscribe(listener1); - - final MockEventListener listener2 = new MockEventListener(); - holderRule.getHolder().subscribe(listener2); - holderRule.assertListenerAdded(listener1, listener2); - - final Object event = "doom"; - final Throwable expected = new NullPointerException(); - holderRule.getHolder().invokeListeners(new Action5() { - @Override - public void call(MockEventListener mockEventListener, Long duration, TimeUnit timeUnit, Throwable throwable, - Object event) { - mockEventListener.onCustomEvent(event, duration, timeUnit, throwable); - } - }, 1, TimeUnit.MICROSECONDS, expected, event); - - assertThat("Listener not invoked.", listener1.getEventInvocationCount(), is(1)); - assertThat("Listener not invoked with duration.", listener1.getDuration(), is(1L)); - assertThat("Listener not invoked with time unit.", listener1.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - assertThat("Listener not invoked with error.", listener1.getRecievedError(), is(expected)); - assertThat("Listener not invoked with argument.", listener1.getCustomEvent(), is(event)); - - assertThat("Second listener not invoked.", listener2.getEventInvocationCount(), is(1)); - assertThat("Second listener not invoked with duration.", listener2.getDuration(), is(1L)); - assertThat("Second listener not invoked with time unit.", listener2.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - assertThat("Second listener not invoked with error.", listener2.getRecievedError(), is(expected)); - assertThat("Second listener not invoked with argument.", listener2.getCustomEvent(), is(event)); - } - - @Test(timeout = 60000) - public void testInvokeListenersWithDurationArgRaiseException() throws Exception { - final MockEventListener listener1 = new MockEventListener(true); - holderRule.getHolder().subscribe(listener1); - - final MockEventListener listener2 = new MockEventListener(); - holderRule.getHolder().subscribe(listener2); - holderRule.assertListenerAdded(listener1, listener2); - - final String arg = "doom"; - holderRule.getHolder().invokeListeners(new Action4() { - @Override - public void call(MockEventListener mockEventListener, Long duration, TimeUnit timeUnit, String arg) { - mockEventListener.anEventWithDurationAndArg(duration, timeUnit, arg); - } - }, 1, TimeUnit.MICROSECONDS, arg); - - assertThat("Listener not invoked.", listener1.getEventInvocationCount(), is(1)); - assertThat("Listener not invoked with duration.", listener1.getDuration(), is(1L)); - assertThat("Listener not invoked with time unit.", listener1.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - assertThat("Listener not invoked with argument.", listener1.getArg(), is(arg)); - - assertThat("Second listener not invoked.", listener2.getEventInvocationCount(), is(1)); - assertThat("Second listener not invoked with duration.", listener2.getDuration(), is(1L)); - assertThat("Second listener not invoked with time unit.", listener2.getTimeUnit(), is(TimeUnit.MICROSECONDS)); - assertThat("Second listener not invoked with argument.", listener2.getArg(), is(arg)); - } - - @Test(timeout = 60000) - public void testInvokeListenersWithArg() throws Exception { - final MockEventListener listener = new MockEventListener(); - holderRule.getHolder().subscribe(listener); - holderRule.assertListenerAdded(listener); - - final String arg = "doom"; - holderRule.getHolder().invokeListeners(new Action2() { - @Override - public void call(MockEventListener mockEventListener, String arg) { - mockEventListener.anEventWithArg(arg); - } - }, arg); - - assertThat("Listener not invoked.", listener.getEventInvocationCount(), is(1)); - assertThat("Listener not invoked with argument.", listener.getArg(), is(arg)); - } - - @Test(timeout = 60000) - public void testInvokeListenersWithExceptionAndArg() throws Exception { - final MockEventListener listener = new MockEventListener(); - holderRule.getHolder().subscribe(listener); - holderRule.assertListenerAdded(listener); - - final Object event = "doom"; - final Throwable expected = new NullPointerException(); - holderRule.getHolder().invokeListeners(new Action3() { - @Override - public void call(MockEventListener mockEventListener, Throwable throwable, Object arg) { - mockEventListener.onCustomEvent(arg, throwable); - } - }, expected, event); - - assertThat("Listener not invoked.", listener.getEventInvocationCount(), is(1)); - assertThat("Listener not invoked with argument.", listener.getCustomEvent(), equalTo(event)); - assertThat("Listener not invoked with exception.", listener.getRecievedError(), is(expected)); - } - - @Test(timeout = 60000) - public void testInvokeListenersMultiWithArg() throws Exception { - final MockEventListener listener1 = new MockEventListener(); - holderRule.getHolder().subscribe(listener1); - - final MockEventListener listener2 = new MockEventListener(); - holderRule.getHolder().subscribe(listener2); - holderRule.assertListenerAdded(listener1, listener2); - - final String arg = "doom"; - holderRule.getHolder().invokeListeners(new Action2() { - @Override - public void call(MockEventListener mockEventListener, String arg) { - mockEventListener.anEventWithArg(arg); - } - }, arg); - - assertThat("Listener not invoked.", listener1.getEventInvocationCount(), is(1)); - assertThat("Listener not invoked with argument.", listener1.getArg(), is(arg)); - - assertThat("Second listener not invoked.", listener2.getEventInvocationCount(), is(1)); - assertThat("Second listener not invoked with argument.", listener2.getArg(), is(arg)); - } - - @Test(timeout = 60000) - public void testInvokeListenersWithArgAndRaiseException() throws Exception { - final MockEventListener listener1 = new MockEventListener(true); - holderRule.getHolder().subscribe(listener1); - - final MockEventListener listener2 = new MockEventListener(); - holderRule.getHolder().subscribe(listener2); - holderRule.assertListenerAdded(listener1, listener2); - - final String arg = "doom"; - holderRule.getHolder().invokeListeners(new Action2() { - @Override - public void call(MockEventListener mockEventListener, String arg) { - mockEventListener.anEventWithArg(arg); - } - }, arg); - - assertThat("Listener not invoked.", listener1.getEventInvocationCount(), is(1)); - assertThat("Listener not invoked with argument.", listener1.getArg(), is(arg)); - - assertThat("Second listener not invoked.", listener2.getEventInvocationCount(), is(1)); - assertThat("Second listener not invoked with argument.", listener2.getArg(), is(arg)); - } - - @Test(timeout = 60000) - public void testDuplicateListeners() throws Exception { - ListenerWithSub l = holderRule.addAListener(); - - holderRule.assertListenerAdded(l.listener); - - holderRule.getHolder().subscribe(l.listener); - - assertThat("Duplicate listener added.", holderRule.getHolder().getActualListenersList(), hasSize(1)); - - l.subscription.unsubscribe(); - - assertThat("Listener not removed on unsubscribe.", holderRule.getHolder().getAllListeners(), is(empty())); - } - - @Test(timeout = 60000) - public void testCopy() throws Exception { - final MockEventListener listener = new MockEventListener(); - Subscription subscription = holderRule.getHolder().subscribe(listener); - holderRule.assertListenerAdded(listener); - - final ListenersHolder copy = holderRule.getHolder().copy(); - - assertThat("Holder not copied", copy, is(not(holderRule.getHolder()))); - assertThat("Listeners list not copied", copy.getActualListenersList(), - not(sameInstance(holderRule.getHolder().getActualListenersList()))); - - final Collection allListenersCopied = copy.getAllListeners(); - - assertThat("Registered listeners not copied", allListenersCopied, contains(listener)); - - subscription.unsubscribe(); - - assertThat("Not removed from copy on unsubscribe.", copy.getAllListeners(), not(contains(listener))); - } - -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/CookiesHolderTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/CookiesHolderTest.java deleted file mode 100644 index 793e9ac..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/CookiesHolderTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http; - -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.cookie.ClientCookieEncoder; -import io.netty.handler.codec.http.cookie.Cookie; -import io.netty.handler.codec.http.cookie.DefaultCookie; -import org.junit.Test; - -import java.util.Map; -import java.util.Set; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class CookiesHolderTest { - - @Test(timeout = 60000) - public void testClientResponseHolder() throws Exception { - DefaultHttpResponse headers = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - String cookie1Name = "PREF"; - String cookie1Value = "ID=a95756377b78e75e:FF=0:TM=1392709628:LM=1392709628:S=a5mOVvTB7DBkexgi"; - String cookie1Header = cookie1Name + '=' + cookie1Value - + "; expires=Thu, 18-Feb-2016 07:47:08 GMT;"; - headers.headers().add(SET_COOKIE, cookie1Header); - - CookiesHolder holder = CookiesHolder.newClientResponseHolder(headers.headers()); - Map> cookies = holder.getAllCookies(); - - assertThat("Cookies are null.", cookies, is(notNullValue())); - assertThat("Cookies are empty.", cookies.values(), is(not(empty()))); - - Set cookies1 = cookies.get(cookie1Name); - - assertThat("No cookies found with name: " + cookie1Name, cookies1, is(notNullValue())); - assertThat("Unexpected number of cookies found.", cookies1, hasSize(1)); - - Cookie cookieFound = cookies1.iterator().next(); - - assertThat("Unexpected cookie name.", cookieFound.name(), equalTo(cookie1Name)); - assertThat("Unexpected cookie value.", cookieFound.value(), equalTo(cookie1Value)); - } - - @Test(timeout = 60000) - public void testServerRequestHolder() throws Exception { - DefaultHttpRequest headers = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, ""); - String cookie1Name = "PREF"; - String cookie1Value = "ID=a95756377b78e75e:FF=0:TM=1392709628:LM=1392709628:S=a5mOVvTB7DBkexgi"; - Cookie cookie = new DefaultCookie(cookie1Name, cookie1Value); - - headers.headers().add(COOKIE, ClientCookieEncoder.STRICT.encode(cookie)); - - CookiesHolder holder = CookiesHolder.newServerRequestHolder(headers.headers()); - Map> cookies = holder.getAllCookies(); - - assertThat("Cookies are null.", cookies, is(notNullValue())); - assertThat("Cookies are empty.", cookies.values(), is(not(empty()))); - - Set cookies1 = cookies.get(cookie1Name); - assertThat("No cookies found with name: " + cookie1Name, cookies1, is(notNullValue())); - assertThat("Unexpected number of cookies found.", cookies1, hasSize(1)); - - Cookie cookieFound = cookies1.iterator().next(); - - assertThat("Unexpected cookie name.", cookieFound.name(), equalTo(cookie1Name)); - assertThat("Unexpected cookie value.", cookieFound.value(), equalTo(cookie1Value)); - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/EventListenerTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/EventListenerTest.java deleted file mode 100644 index e5ea0a3..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/EventListenerTest.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.logging.LogLevel; -import io.reactivex.netty.protocol.http.client.events.HttpClientEventsListener; -import io.reactivex.netty.protocol.http.server.HttpServer; -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 org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.Observable; -import rx.functions.Func1; -import rx.observers.TestSubscriber; - -import java.net.SocketAddress; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class EventListenerTest { - - @Rule - public final HttpServerRule rule = new HttpServerRule(); - - @Test(timeout = 60000) - public void testEventListener() throws Exception { - HttpClient client = HttpClient.newClient(rule.serverAddress); - - assertListenerCalled(client); - } - - @Test(timeout = 60000) - public void testEventListenerPostCopy() throws Exception { - HttpClient client = HttpClient.newClient(rule.serverAddress) - .enableWireLogging("test", LogLevel.ERROR); - - assertListenerCalled(client); - } - - @Test(timeout = 60000) - public void testSubscriptionPreCopy() throws Exception { - HttpClient client = HttpClient.newClient(rule.serverAddress); - - MockHttpClientEventsListener listener = subscribe(client); - - client = client.enableWireLogging("test", LogLevel.DEBUG); - - connectAndAssertListenerInvocation(client, listener); - } - - private static void assertListenerCalled(HttpClient client) { - MockHttpClientEventsListener listener = subscribe(client); - connectAndAssertListenerInvocation(client, listener); - } - - private static void connectAndAssertListenerInvocation(HttpClient client, - MockHttpClientEventsListener listener) { - TestSubscriber subscriber = new TestSubscriber<>(); - client.createGet("") - .flatMap(new Func1, Observable>() { - @Override - public Observable call(HttpClientResponse r) { - return r.getContent(); - } - }) - .take(1) - .subscribe(subscriber); - - subscriber.awaitTerminalEvent(); - subscriber.assertNoErrors(); - - assertThat("HTTP methods not invoked on the listener.", listener.httpListenerInvoked, is(true)); - assertThat("TCP methods not invoked on the listener.", listener.tcpListenerInvoked, is(true)); - } - - private static MockHttpClientEventsListener subscribe(HttpClient client) { - MockHttpClientEventsListener listener = new MockHttpClientEventsListener(); - client.subscribe(listener); - return listener; - } - - public static class HttpServerRule extends ExternalResource { - - private SocketAddress serverAddress; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - serverAddress = HttpServer.newServer().enableWireLogging("test", LogLevel.ERROR) - .start(new RequestHandler() { - @Override - public Observable handle(HttpServerRequest request, - HttpServerResponse response) { - return response.writeString(Observable.just("Hello")); - } - }).getServerAddress(); - base.evaluate(); - } - }; - } - } - - private static class MockHttpClientEventsListener extends HttpClientEventsListener { - - private volatile boolean httpListenerInvoked; - private volatile boolean tcpListenerInvoked; - - @Override - public void onResponseHeadersReceived(int responseCode, long duration, TimeUnit timeUnit) { - httpListenerInvoked = true; - } - - @Override - public void onByteRead(long bytesRead) { - tcpListenerInvoked = true; - } - } - -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/HttpClientPoolTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/HttpClientPoolTest.java deleted file mode 100644 index 989949a..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/HttpClientPoolTest.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.DefaultLastHttpContent; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.reactivex.netty.client.pool.FIFOIdleConnectionsHolder; -import io.reactivex.netty.client.pool.PoolConfig; -import io.reactivex.netty.client.pool.PooledConnection; -import io.reactivex.netty.protocol.http.client.internal.HttpClientResponseImpl; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.observers.TestSubscriber; - -import java.nio.channels.ClosedChannelException; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class HttpClientPoolTest { - - @Rule - public final PooledHttpClientRule clientRule = new PooledHttpClientRule(); - - @Test(timeout = 60000) - public void testBasicAcquireRelease() throws Exception { - - clientRule.assertIdleConnections(0); - - final HttpClientRequest request1 = clientRule.getHttpClient().createGet("/"); - TestSubscriber subscriber = clientRule.sendRequestAndDiscardResponseContent(request1); - - clientRule.assertIdleConnections(0); // No idle connections post connect - clientRule.assertRequestHeadersWritten(HttpMethod.GET, "/"); - - clientRule.feedResponseAndComplete(); - - subscriber.assertTerminalEvent(); - subscriber.assertNoErrors(); - - clientRule.getLastCreatedChannel().runPendingTasks(); - - clientRule.assertIdleConnections(1); - } - - @Test(timeout = 60000) - public void testBasicAcquireReleaseWithServerClose() throws Exception { - - clientRule.assertIdleConnections(0); - - final HttpClientRequest request1 = clientRule.getHttpClient().createGet("/"); - TestSubscriber subscriber = clientRule.sendRequestAndDiscardResponseContent(request1); - - clientRule.assertIdleConnections(0); // No idle connections post connect - clientRule.assertRequestHeadersWritten(HttpMethod.GET, "/"); - - clientRule.getLastCreatedChannel().close().await(); - - subscriber.assertTerminalEvent(); - assertThat("On complete sent instead of onError", subscriber.getOnCompletedEvents(), is(empty())); - assertThat("Unexpected error notifications count.", subscriber.getOnErrorEvents(), hasSize(1)); - assertThat("Unexpected error notification.", subscriber.getOnErrorEvents().get(0), - is(instanceOf(ClosedChannelException.class))); - - clientRule.getLastCreatedChannel().runPendingTasks(); - - clientRule.assertIdleConnections(0); // Since, channel is closed, it should be discarded. - } - - @Test(timeout = 60000) - public void testCloseOnKeepAliveTimeout() throws Exception { - - clientRule.assertIdleConnections(0); - - final HttpClientRequest request1 = clientRule.getHttpClient().createGet("/"); - - TestSubscriber> responseSub = clientRule.sendRequest(request1); - - clientRule.assertIdleConnections(0); // No idle connections post connect - clientRule.assertRequestHeadersWritten(HttpMethod.GET, "/"); - - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - response.headers().set(HttpClientResponseImpl.KEEP_ALIVE_HEADER_NAME, - HttpClientResponseImpl.KEEP_ALIVE_TIMEOUT_HEADER_ATTR + "=0"); - clientRule.feedResponseAndComplete(response); - - HttpClientResponse resp = clientRule.discardResponseContent(responseSub); - Channel nettyChannel = resp.unsafeNettyChannel(); - - clientRule.getLastCreatedChannel().runPendingTasks(); - - // Close is while release, so this should be post running pending tasks - assertThat("Channel not closed.", nettyChannel.isOpen(), is(false)); - clientRule.assertIdleConnections(0); // Since, the channel is closed - } - - @Test(timeout = 60000) - public void testReuse() throws Exception { - clientRule.assertIdleConnections(0); - - Channel channel1 = clientRule.sendRequestAndGetChannel(); - - clientRule.getLastCreatedChannel().runPendingTasks(); - - clientRule.assertIdleConnections(1); - - Channel channel2 = clientRule.sendRequestAndGetChannel(); - - assertThat("Connection was not reused.", channel2, is(channel1)); - } - - public static class PooledHttpClientRule extends HttpClientRule { - - private FIFOIdleConnectionsHolder idleConnHolder; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - idleConnHolder = new FIFOIdleConnectionsHolder<>(); - PoolConfig pConfig = new PoolConfig<>(); - pConfig.idleConnectionsHolder(idleConnHolder); - - setupPooledConnectionFactory(pConfig); // sets the client et al. - - base.evaluate(); - } - }; - } - - public void assertIdleConnections(int expectedCount) { - TestSubscriber> testSub = new TestSubscriber<>(); - idleConnHolder.peek().subscribe(testSub); - - testSub.assertTerminalEvent(); - testSub.assertNoErrors(); - - assertThat("Unexpected number of connections in the holder.", testSub.getOnNextEvents(), - hasSize(expectedCount)); - } - - protected Channel sendRequestAndGetChannel() { - final HttpClientRequest request1 = getHttpClient().createGet("/"); - - TestSubscriber> respSub = sendRequest(request1); - - feedResponseHeaders(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK), - getLastCreatedChannelWithFeeder()); - - respSub.awaitTerminalEvent(); - - assertIdleConnections(0); // No idle connections post connect - assertRequestHeadersWritten(HttpMethod.GET, "/"); - - feedResponse(new DefaultLastHttpContent()); - - final HttpClientResponse response = discardResponseContent(respSub); - - return response.unsafeNettyChannel(); - } - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/HttpClientRule.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/HttpClientRule.java deleted file mode 100644 index d6ef598..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/HttpClientRule.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.DefaultHttpContent; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.DefaultLastHttpContent; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.logging.LogLevel; -import io.reactivex.netty.client.Host; -import io.reactivex.netty.client.pool.PoolConfig; -import io.reactivex.netty.client.pool.SingleHostPoolingProviderFactory; -import io.reactivex.netty.test.util.embedded.EmbeddedChannelProvider; -import io.reactivex.netty.test.util.embedded.EmbeddedChannelWithFeeder; -import org.junit.Assert; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.Observable; -import rx.functions.Func1; -import rx.observers.TestSubscriber; - -import java.net.InetSocketAddress; -import java.nio.charset.Charset; -import java.util.List; -import java.util.regex.Pattern; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class HttpClientRule extends ExternalResource { - - private EmbeddedChannelProvider channelProvider; - private HttpClient httpClient; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - channelProvider = new EmbeddedChannelProvider(); - httpClient = HttpClient.newClient(new InetSocketAddress(0)) - .enableWireLogging("test", LogLevel.ERROR) - .channelProvider(channelProvider.asFactory()); - base.evaluate(); - } - }; - } - - public void setupPooledConnectionFactory(final PoolConfig pConfig) { - channelProvider = new EmbeddedChannelProvider(); - httpClient = HttpClient.newClient(SingleHostPoolingProviderFactory.create(pConfig), - Observable.just(new Host(new InetSocketAddress(0)))) - .channelProvider(channelProvider.asFactory()); - } - - public HttpClient getHttpClient() { - return httpClient; - } - - public EmbeddedChannel getLastCreatedChannel() { - return getLastCreatedChannelWithFeeder().getChannel(); - } - - public EmbeddedChannelWithFeeder getLastCreatedChannelWithFeeder() { - List createdChannels = getCreatedChannels(); - return createdChannels.get(createdChannels.size() - 1); - } - - public List getCreatedChannels() { - return channelProvider.getCreatedChannels(); - } - - public TestSubscriber> sendRequest(Observable> request) { - TestSubscriber> testSubscriber = new TestSubscriber<>(); - request.subscribe(testSubscriber); - testSubscriber.assertNoErrors(); - return testSubscriber; - } - - public TestSubscriber sendRequestAndGetContent(Observable> request) { - TestSubscriber testSubscriber = new TestSubscriber<>(); - request.flatMap(new Func1, Observable>() { - @Override - public Observable call(HttpClientResponse response) { - return response.getContent() - .map(new Func1() { - @Override - public String call(ByteBuf byteBuf) { - return byteBuf.toString(Charset.defaultCharset()); - } - }); - } - }).subscribe(testSubscriber); - testSubscriber.assertNoErrors(); - return testSubscriber; - } - - public TestSubscriber sendRequestAndDiscardResponseContent(HttpClientRequest request) { - TestSubscriber testSubscriber = new TestSubscriber<>(); - - request.flatMap(new Func1, Observable>() { - @Override - public Observable call(HttpClientResponse clientResponse) { - return clientResponse.discardContent(); - } - }).subscribe(testSubscriber); - testSubscriber.assertNoErrors(); - return testSubscriber; - } - - public TestSubscriber discardResponseContent(HttpClientResponse response) { - - TestSubscriber testSubscriber = new TestSubscriber<>(); - - response.discardContent().subscribe(testSubscriber); - return testSubscriber; - } - - public HttpClientResponse discardResponseContent(TestSubscriber> responseSub) { - - responseSub.awaitTerminalEvent(); - responseSub.assertTerminalEvent(); - responseSub.assertNoErrors(); - - HttpClientResponse resp = responseSub.getOnNextEvents().get(0); - - TestSubscriber testSubscriber = new TestSubscriber<>(); - - resp.discardContent().subscribe(testSubscriber); - - testSubscriber.awaitTerminalEvent(); - testSubscriber.assertTerminalEvent(); - testSubscriber.assertNoErrors(); - - return resp; - } - - public void feedResponse(HttpContent... content) { - for (HttpContent httpContent : content) { - getLastCreatedChannelWithFeeder().getFeeder().addToTheFeed(httpContent); - } - } - - public void feedResponse(HttpResponse response, HttpContent content) { - getLastCreatedChannelWithFeeder().getFeeder().addToTheFeed(response, content); - } - - public void feedResponseHeaders(HttpResponse response, EmbeddedChannelWithFeeder channelWithFeeder) { - channelWithFeeder.getFeeder().addToTheFeed(response); - } - - public void feedResponseHeaders(HttpResponse response) { - feedResponseHeaders(response, getLastCreatedChannelWithFeeder()); - } - - public void feedResponseAndComplete(String... content) { - feedResponseHeaders(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK), - getLastCreatedChannelWithFeeder()); - for (String contentStr : content) { - ByteBuf contentBuf = Unpooled.buffer().writeBytes(contentStr.getBytes()); - feedResponse(new DefaultHttpContent(contentBuf)); - } - - feedResponse(new DefaultLastHttpContent()); - } - - public void feedResponseAndComplete(HttpResponse response, HttpContent content) { - feedResponseAndComplete(response, content, getLastCreatedChannelWithFeeder()); - } - - public void feedResponseAndComplete(HttpResponse response, HttpContent content, - EmbeddedChannelWithFeeder channelWithFeeder) { - channelWithFeeder.getFeeder().addToTheFeed(response, content, new DefaultLastHttpContent()); - } - - public void feedResponseAndComplete(HttpResponse response) { - feedResponseAndComplete(response, getLastCreatedChannelWithFeeder()); - } - - public void feedResponseAndComplete(HttpResponse response, EmbeddedChannelWithFeeder channelWithFeeder) { - channelWithFeeder.getFeeder().addToTheFeed(response, new DefaultLastHttpContent()); - } - - public void assertRequestHeadersWritten(HttpMethod method, String uri) { - - boolean found = false; - Object outbound; - final String expectedFirstLineStart = method.name().toUpperCase() + ' ' + uri; - String data = null; - - while ((outbound = getLastCreatedChannel().readOutbound()) != null) { - if (outbound instanceof ByteBuf) { - ByteBuf bb = (ByteBuf) outbound; - data = bb.toString(Charset.defaultCharset()); - if (data.startsWith(expectedFirstLineStart)) { - found = true; - break; - } - } - } - - assertThat("Unexpected HTTP method & URI for the written request.", data, - startsWith(expectedFirstLineStart)); - - if (!found) { - assertThat("Request not written.", outbound, is(notNullValue())); - } - } - - public void assertContentWritten(String contentStr) { - boolean found = false; - Object outbound; - String data = null; - - while ((outbound = getLastCreatedChannel().readOutbound()) != null) { - if (outbound instanceof ByteBuf) { - ByteBuf bb = (ByteBuf) outbound; - data = bb.toString(Charset.defaultCharset()); - if (data.equalsIgnoreCase(contentStr)) { - found = true; - break; - } - } - } - - assertThat("Unexpected HTTP content.", data, equalToIgnoringCase(contentStr)); - - if (!found) { - assertThat("Content not written.", outbound, is(notNullValue())); - } - } - - public void assertEmptyBodyWithContentLengthZero() { - assertBodyWithContentLength(0, ""); - } - - public void assertBodyWithContentLength(int contentLength, String body) { - Pattern headerBlock = Pattern.compile("^(.*?\r\n)*?\r\n", Pattern.MULTILINE); - Object outbound; - String data = ""; - - while ((outbound = getLastCreatedChannel().readOutbound()) != null) { - if (outbound instanceof ByteBuf) { - ByteBuf bb = (ByteBuf) outbound; - data += bb.toString(Charset.defaultCharset()); - } - } - - if (!data.contains("content-length: " + contentLength + "\r\n")) { - Assert.fail("Missing header 'content-length: " + contentLength + "'"); - } - if (data.contains("transfer-encoding: chunked\r\n")) { - Assert.fail("Unexpected header 'transfer-encoding: chunked'"); - } - if (!headerBlock.matcher(data).replaceFirst("").equals(body)) { - Assert.fail("Unexpected body content '" + headerBlock.matcher(data).replaceFirst("") + "'"); - } - } - - public void assertEmptyBodyWithSingleChunk() { - assertChunks(); - } - - public void assertChunks(String... chunks) { - Pattern headerBlock = Pattern.compile("^(.*?\r\n)*?\r\n", Pattern.MULTILINE); - Object outbound; - String data = ""; - - while ((outbound = getLastCreatedChannel().readOutbound()) != null) { - if (outbound instanceof ByteBuf) { - ByteBuf bb = (ByteBuf) outbound; - data += bb.toString(Charset.defaultCharset()); - } - } - - if (data.contains("content-length: 0\r\n")) { - Assert.fail("Unexpected header 'content-length: 0'"); - } - if (!data.contains("transfer-encoding: chunked\r\n")) { - Assert.fail("Missing header 'transfer-encoding: chunked'"); - } - String expectedChunkContent = ""; - for (String c : chunks) { - expectedChunkContent += c.getBytes().length + "\r\n"; - expectedChunkContent += c + "\r\n"; - } - expectedChunkContent += "0\r\n\r\n"; - if (!headerBlock.matcher(data).replaceFirst("").equals(expectedChunkContent)) { - Assert.fail("Unexpected body content '" + headerBlock.matcher(data).replaceFirst("") + "'"); - } - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/HttpClientTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/HttpClientTest.java deleted file mode 100644 index 633b4fc..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/HttpClientTest.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.timeout.ReadTimeoutException; -import io.reactivex.netty.client.Host; -import io.reactivex.netty.client.pool.SingleHostPoolingProviderFactory; -import io.reactivex.netty.protocol.http.server.HttpServerRequest; -import io.reactivex.netty.protocol.http.server.HttpServerResponse; -import io.reactivex.netty.protocol.http.server.HttpServerRule; -import io.reactivex.netty.protocol.http.server.RequestHandler; -import org.junit.Rule; -import org.junit.Test; -import rx.Observable; -import rx.functions.Func0; -import rx.observers.TestSubscriber; - -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class HttpClientTest { - - @Rule - public final HttpClientRule clientRule = new HttpClientRule(); - - @Rule - public final HttpServerRule serverRule = new HttpServerRule(); - - @Test(timeout = 60000) - public void testCloseOnResponseComplete() throws Exception { - - HttpClientRequest request = clientRule.getHttpClient().createGet("/"); - - TestSubscriber testSubscriber = clientRule.sendRequestAndDiscardResponseContent(request); - - clientRule.assertRequestHeadersWritten(HttpMethod.GET, "/"); - HttpResponse nettyResponse = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - clientRule.feedResponseAndComplete(nettyResponse); - - testSubscriber.awaitTerminalEvent(); - testSubscriber.assertNoErrors(); - - assertThat("Channel not closed after response completion.", clientRule.getLastCreatedChannel().isOpen(), is(false)); - } - - @Test(timeout = 60000) - public void testResponseContent() throws Exception { - - HttpClientRequest request = clientRule.getHttpClient().createGet("/"); - - TestSubscriber testSubscriber = clientRule.sendRequestAndGetContent(request); - - clientRule.assertRequestHeadersWritten(HttpMethod.GET, "/"); - - final String content = "Hello"; - clientRule.feedResponseAndComplete(content); - - testSubscriber.awaitTerminalEvent(); - testSubscriber.assertNoErrors(); - - assertThat("Unexpected response content count.", testSubscriber.getOnNextEvents(), hasSize(1)); - assertThat("Unexpected response content.", testSubscriber.getOnNextEvents(), contains(content)); - } - - @Test(timeout = 60000) - public void testResponseContentMultipleChunks() throws Exception { - - HttpClientRequest request = clientRule.getHttpClient().createGet("/"); - - TestSubscriber testSubscriber = clientRule.sendRequestAndGetContent(request); - - clientRule.assertRequestHeadersWritten(HttpMethod.GET, "/"); - - final String content1 = "Hello1"; - final String content2 = "Hello2"; - clientRule.feedResponseAndComplete(content1, content2); - - testSubscriber.awaitTerminalEvent(); - testSubscriber.assertNoErrors(); - - assertThat("Unexpected response content count.", testSubscriber.getOnNextEvents(), hasSize(2)); - assertThat("Unexpected response content.", testSubscriber.getOnNextEvents(), contains(content1, content2)); - } - - @Test(timeout = 60000) - public void testAggregatedContent() throws Exception { - - HttpClientRequest request = clientRule.getHttpClient() - .addChannelHandlerLast("aggregator", new Func0() { - @Override - public ChannelHandler call() { - return new HttpObjectAggregator(1024); - } - }) - .createGet("/"); - - TestSubscriber testSubscriber = clientRule.sendRequestAndGetContent(request); - - clientRule.assertRequestHeadersWritten(HttpMethod.GET, "/"); - - final String content1 = "Hello1"; - final String content2 = "Hello2"; - clientRule.feedResponseAndComplete(content1, content2); - - testSubscriber.awaitTerminalEvent(); - testSubscriber.assertNoErrors(); - - assertThat("Unexpected response content count.", testSubscriber.getOnNextEvents(), hasSize(1)); - assertThat("Unexpected response content.", testSubscriber.getOnNextEvents().get(0), - containsString(content1)); - assertThat("Unexpected response content.", testSubscriber.getOnNextEvents().get(0), - containsString(content2)); - } - - @Test(timeout = 60000) - public void testNoContentSubscribe() throws Exception { - HttpClientRequest request = clientRule.getHttpClient().createGet("/"); - - TestSubscriber> testSubscriber = clientRule.sendRequest(request); - clientRule.assertRequestHeadersWritten(HttpMethod.GET, "/"); - - clientRule.feedResponseHeaders(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)); - - testSubscriber.assertTerminalEvent(); - } - - @Test(timeout = 60000) - public void testPost() throws Exception { - String contentStr = "Hello"; - Observable> request = clientRule.getHttpClient() - .createPost("/") - .writeStringContent(Observable.just(contentStr)); - - TestSubscriber testSubscriber = clientRule.sendRequestAndGetContent(request); - - clientRule.assertRequestHeadersWritten(HttpMethod.POST, "/"); - clientRule.assertContentWritten(contentStr); - - clientRule.feedResponseAndComplete(); - - testSubscriber.awaitTerminalEvent(); - testSubscriber.assertNoErrors(); - - assertThat("Unexpected response content count.", testSubscriber.getOnNextEvents(), is(empty())); - } - - @Test(timeout = 60000) - public void testReadTimeoutNoPooling() throws Exception { - - startServerThatNeverReplies(); - - HttpClientRequest request = HttpClient.newClient(serverRule.getServerAddress()) - .readTimeOut(1, TimeUnit.SECONDS) - .createGet("/"); - - TestSubscriber testSubscriber = clientRule.sendRequestAndDiscardResponseContent(request); - - testSubscriber.awaitTerminalEvent(); - - assertThat("On complete invoked, instead of error.", testSubscriber.getOnCompletedEvents(), is(empty())); - assertThat("Unexpected onError count.", testSubscriber.getOnErrorEvents(), hasSize(1)); - assertThat("Unexpected exception.", testSubscriber.getOnErrorEvents().get(0), - is(instanceOf(ReadTimeoutException.class))); - } - - @Test(timeout = 60000) - public void testReadTimeoutWithPooling() throws Exception { - - startServerThatNeverReplies(); - - HttpClientRequest request = - HttpClient.newClient(SingleHostPoolingProviderFactory.createUnbounded(), - Observable.just(new Host(serverRule.getServerAddress()))) - .readTimeOut(1, TimeUnit.SECONDS) - .createGet("/"); - - TestSubscriber testSubscriber = clientRule.sendRequestAndDiscardResponseContent(request); - - testSubscriber.awaitTerminalEvent(); - - assertThat("On complete invoked, instead of error.", testSubscriber.getOnCompletedEvents(), is(empty())); - assertThat("Unexpected onError count.", testSubscriber.getOnErrorEvents(), hasSize(1)); - assertThat("Unexpected exception.", testSubscriber.getOnErrorEvents().get(0), - is(instanceOf(ReadTimeoutException.class))); - } - - @Test(timeout = 60000) - public void testRequestWithNoContentLengthHeaderOrContentReturnsEmptyBody() { - clientRule.sendRequest(clientRule.getHttpClient().createGet("/")); - clientRule.assertEmptyBodyWithContentLengthZero(); - } - - @Test(timeout = 60000) - public void testRequestWithNoContentLengthHeaderAndContentReturnsContentChunkAndSingleEmptyChunk() { - clientRule.sendRequest(clientRule.getHttpClient().createGet("/") - .writeStringContent(Observable.just("Hello"))); - clientRule.assertChunks("Hello"); - } - - @Test(timeout = 60000) - public void testRequestWithContentLengthReturnsRawBody() { - clientRule.sendRequest(clientRule.getHttpClient().createGet("/") - .setHeader(HttpHeaderNames.CONTENT_LENGTH, 5) - .writeStringContent(Observable.just("Hello"))); - clientRule.assertBodyWithContentLength(5, "Hello"); - } - - @Test(timeout = 60000) - public void testRequestWithZeroContentLengthReturnsEmptyBody() { - clientRule.sendRequest(clientRule.getHttpClient().createGet("/").setHeader(HttpHeaderNames.CONTENT_LENGTH, 0)); - clientRule.assertEmptyBodyWithContentLengthZero(); - } - - @Test(timeout = 60000) - public void testRequestWithOnlyPositiveContentLengthReturnsEmptyBody() { - clientRule.sendRequest(clientRule.getHttpClient().createGet("/").setHeader(HttpHeaderNames.CONTENT_LENGTH, 5)); - clientRule.assertEmptyBodyWithContentLengthZero(); - } - - protected void startServerThatNeverReplies() { - serverRule.startServer(new RequestHandler() { - @Override - public Observable handle(HttpServerRequest request, HttpServerResponse response) { - return Observable.never(); - } - }); - } - - @Test(timeout = 60000) - public void testLargeHeaders() throws Exception { - - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/HttpRedirectTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/HttpRedirectTest.java deleted file mode 100644 index 508c044..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/HttpRedirectTest.java +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.reactivex.netty.client.pool.PoolConfig; -import io.reactivex.netty.test.util.embedded.EmbeddedChannelWithFeeder; -import org.junit.Rule; -import org.junit.Test; -import rx.observers.TestSubscriber; - -import java.util.List; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class HttpRedirectTest { - - @Rule - public final HttpClientRule clientRule = new HttpClientRule(); - - @Test(timeout = 60000) - public void testNoLocation() throws Exception { - - final String requestUri = "/"; - - TestSubscriber> subscriber = sendRequest(requestUri); - - assertRequestWritten(requestUri); - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.SEE_OTHER); - clientRule.feedResponseAndComplete(response); - - subscriber.awaitTerminalEvent(); - - assertThat("Unexpected error notifications count.", subscriber.getOnErrorEvents(), hasSize(1)); - assertThat("Unexpected error.", subscriber.getOnErrorEvents().get(0), - is(instanceOf(HttpRedirectException.class))); - - } - - @Test(timeout = 60000) - public void testInvalidRedirectLocation() throws Exception { - - final String requestUri = "/"; - TestSubscriber> subscriber = sendRequest(requestUri); - - assertRequestWritten(requestUri); - sendRedirects(" "); // blank is an invalid URI - - subscriber.awaitTerminalEvent(); - - assertThat("Unexpected error notifications count.", subscriber.getOnErrorEvents(), hasSize(1)); - assertThat("Unexpected error.", subscriber.getOnErrorEvents().get(0), - is(instanceOf(HttpRedirectException.class))); - - } - - @Test(timeout = 60000) - public void testTooManyRedirect() throws Throwable { - - final String requestUri = "/"; - TestSubscriber> subscriber = sendRequest(requestUri); - - assertRequestWritten(requestUri); - sendRedirects("/blah", "/blah"); - - subscriber.awaitTerminalEvent(); - - assertThat("Unexpected error notifications count.", subscriber.getOnErrorEvents(), hasSize(1)); - assertThat("Unexpected error.", subscriber.getOnErrorEvents().get(0), - is(instanceOf(HttpRedirectException.class))); - } - - @Test(timeout = 60000) - public void testRedirectLoop() throws Throwable { - - final String requestUri = "/blah"; - TestSubscriber> subscriber = sendRequest(requestUri); - - assertRequestWritten(requestUri); - sendRedirects(requestUri); - - subscriber.awaitTerminalEvent(); - - assertThat("Unexpected error notifications count.", subscriber.getOnErrorEvents(), hasSize(1)); - assertThat("Unexpected error.", subscriber.getOnErrorEvents().get(0), - is(instanceOf(HttpRedirectException.class))); - } - - @Test(timeout = 60000) - public void testAbsoluteRedirect() throws Throwable { - - final String requestUri = "/blah"; - TestSubscriber> subscriber = sendRequest(requestUri); - - assertRequestWritten(requestUri); - sendRedirects("http://localhost:8888/blah"); - - subscriber.awaitTerminalEvent(); - subscriber.assertNoErrors(); - - assertThat("Unexpected onNext notifications count.", subscriber.getOnNextEvents(), hasSize(1)); - HttpClientResponse response = subscriber.getOnNextEvents().get(0); - assertThat("Unexpected response.", response, is(notNullValue())); - assertThat("Unexpected response status.", response.getStatus().code(), is(HttpResponseStatus.SEE_OTHER.code())); - } - - @Test(timeout = 60000) - public void testRedirectNoConnPool() throws Throwable { - - final String requestUri = "/"; - - HttpClient client = clientRule.getHttpClient().followRedirects(1); - TestSubscriber> subscriber = sendRequest(client, requestUri); - - assertRequestWritten(requestUri); - sendRedirects("/blah", "/blah"); - - subscriber.awaitTerminalEvent(); - - assertThat("Unexpected error notifications count.", subscriber.getOnErrorEvents(), hasSize(1)); - assertThat("Unexpected error.", subscriber.getOnErrorEvents().get(0), - is(instanceOf(HttpRedirectException.class))); - } - - @Test(timeout = 60000) - public void testRedirectWithConnPool() throws Throwable { - PoolConfig pConfig = new PoolConfig().maxConnections(10); - - clientRule.setupPooledConnectionFactory(pConfig); // sets the client et al. - - HttpClient client = clientRule.getHttpClient().followRedirects(1); - - final String requestUri = "/"; - TestSubscriber> subscriber = sendRequest(client, requestUri); - assertRequestWritten(requestUri); - - sendRedirects("/blah", "blah"); - - subscriber.awaitTerminalEvent(); - - assertThat("Unexpected error notifications count.", subscriber.getOnErrorEvents(), hasSize(1)); - assertThat("Unexpected error.", subscriber.getOnErrorEvents().get(0), - is(instanceOf(HttpRedirectException.class))); - } - - @Test(timeout = 60000) - public void testNoRedirect() { - HttpClient client = clientRule.getHttpClient().followRedirects(false); - - final String requestUri = "/"; - TestSubscriber> subscriber = sendRequest(client, requestUri); - - assertRequestWritten(requestUri); - sendRedirects("/blah2"); - - subscriber.awaitTerminalEvent(); - subscriber.assertNoErrors(); - - assertThat("Unexpected onNext notifications count.", subscriber.getOnNextEvents(), hasSize(1)); - HttpClientResponse response = subscriber.getOnNextEvents().get(0); - assertThat("Unexpected response.", response, is(notNullValue())); - assertThat("Unexpected response status.", response.getStatus().code(), is(HttpResponseStatus.SEE_OTHER.code())); - } - - @Test(timeout = 60000) - public void testRedirectPost() throws Throwable { - - final String requestUri = "/"; - TestSubscriber> subscriber = sendRequest(HttpMethod.POST, requestUri); - - final HttpResponseStatus responseStatus = HttpResponseStatus.FOUND; - - clientRule.assertRequestHeadersWritten(HttpMethod.POST, requestUri); - sendRedirects(responseStatus, "/blah"); - - subscriber.awaitTerminalEvent(); - subscriber.assertNoErrors(); - - assertThat("Unexpected onNext notifications count.", subscriber.getOnNextEvents(), hasSize(1)); - HttpClientResponse response = subscriber.getOnNextEvents().get(0); - assertThat("Unexpected response.", response, is(notNullValue())); - assertThat("Unexpected response status.", response.getStatus().code(), is(responseStatus.code())); - } - - @Test(timeout = 60000) - public void testRedirectPostWith303() throws Throwable { - - final String requestUri = "/"; - TestSubscriber> subscriber = sendRequest(HttpMethod.POST, requestUri); - - clientRule.assertRequestHeadersWritten(HttpMethod.POST, requestUri); - sendRedirects(HttpResponseStatus.SEE_OTHER, "/blah"); - - sendResponse(HttpResponseStatus.OK); - - subscriber.awaitTerminalEvent(); - subscriber.assertNoErrors(); - - assertThat("Unexpected onNext notifications count.", subscriber.getOnNextEvents(), hasSize(1)); - HttpClientResponse response = subscriber.getOnNextEvents().get(0); - assertThat("Unexpected response.", response, is(notNullValue())); - assertThat("Unexpected response status.", response.getStatus().code(), is(HttpResponseStatus.OK.code())); - } - - private static TestSubscriber> sendRequest(HttpClient client, - HttpMethod method, String uri) { - final HttpClientRequest req = client.createRequest(method, uri); - TestSubscriber> subscriber = new TestSubscriber<>(); - req.subscribe(subscriber); - subscriber.assertNoErrors(); - return subscriber; - } - - private void assertRequestWritten(String uri) { - clientRule.assertRequestHeadersWritten(HttpMethod.GET, uri); - } - - private static TestSubscriber> sendRequest(HttpClient client, - String uri) { - return sendRequest(client, HttpMethod.GET, uri); - } - - private TestSubscriber> sendRequest(String uri) { - return sendRequest(clientRule.getHttpClient().followRedirects(1), uri); - } - - private TestSubscriber> sendRequest(HttpMethod method, String uri) { - return sendRequest(clientRule.getHttpClient().followRedirects(1), method, uri); - } - - private void sendRedirects(String... locations) { - sendRedirects(HttpResponseStatus.SEE_OTHER, locations); - } - - private void sendRedirects(HttpResponseStatus redirectStatus, String... locations) { - - for (int i = 0; i < locations.length; i++) { - List createdChannels = clientRule.getCreatedChannels(); - assertThat("Not enough channels created by the embedded factory.", createdChannels, - hasSize(greaterThanOrEqualTo(i + 1))); - String location = locations[i]; - EmbeddedChannelWithFeeder channelWithFeeder = createdChannels.get(i); - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, redirectStatus); - response.headers().set(LOCATION, location); - clientRule.feedResponseAndComplete(response, channelWithFeeder); - } - } - - private void sendResponse(HttpResponseStatus redirectStatus) { - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, redirectStatus); - clientRule.feedResponseAndComplete(response); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/RedirectOperatorTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/RedirectOperatorTest.java deleted file mode 100644 index b39e9fb..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/RedirectOperatorTest.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client; - -public class RedirectOperatorTest { -/* - - @Test - public void testMaxRedirects() throws Exception { - - Setup setup = new Setup().setup(HttpResponseStatus.TEMPORARY_REDIRECT); - - Assert.assertEquals("Unexpected redirect count.", 2, setup.getHandler().getRedirectsRequested()); - Assert.assertEquals("Unexpected onComplete calls to redirect subscriber.", 0, setup.getSubscriber().getOnCompletes()); - Assert.assertEquals("Unexpected onNext calls to redirect subscriber.", 0, setup.getSubscriber().getOnNexts()); - Assert.assertEquals("Unexpected onError calls to redirect subscriber.", 1, setup.getSubscriber().getOnErrors()); - } - - @Test - public void testRedirect() throws Exception { - - Setup setup = new Setup().setup(HttpResponseStatus.OK); - - Assert.assertEquals("Unexpected redirect count.", 1, setup.getHandler().getRedirectsRequested()); - Assert.assertEquals("Unexpected onComplete calls to redirect subscriber.", 1, setup.getSubscriber().getOnCompletes()); - Assert.assertEquals("Unexpected onNext calls to redirect subscriber.", 1, setup.getSubscriber().getOnNexts()); - Assert.assertEquals("Unexpected onError calls to redirect subscriber.", 0, setup.getSubscriber().getOnErrors()); - } - - private static class TestableRedirectHandler implements RedirectOperator.RedirectHandler { - - private final HttpClientResponse response; - private final int maxHops; - private final AtomicInteger redirectsRequested = new AtomicInteger(); - - public TestableRedirectHandler(int maxHops, HttpResponseStatus redirectResponseStatus) { - this.maxHops = maxHops; - DefaultHttpResponse nettyResponse = new DefaultHttpResponse(HttpVersion.HTTP_1_1, redirectResponseStatus); - response = new HttpClientResponse(nettyResponse, UnicastContentSubject.createWithoutNoSubscriptionTimeout()); - } - - public TestableRedirectHandler(int maxHops) { - this(maxHops, HttpResponseStatus.TEMPORARY_REDIRECT); - } - - @Override - public Observable> doRedirect(RedirectionContext context, - HttpClientRequest originalRequest, - HttpClient.HttpClientConfig config) { - redirectsRequested.incrementAndGet(); - return Observable.just(response); - } - - @Override - public boolean requiresRedirect(RedirectionContext context, HttpClientResponse response) { - return response.getStatus() == HttpResponseStatus.TEMPORARY_REDIRECT; - } - - @Override - public void validate(RedirectionContext context, HttpClientResponse redirectResponse) { - if(context.getRedirectCount() >= maxHops) { - throw new HttpRedirectException(HttpRedirectException.Reason.TooManyRedirects, ""); - } - } - - private int getRedirectsRequested() { - return redirectsRequested.get(); - } - } - - private static class UnsafeRedirectSubscriber extends Subscriber> { - private final AtomicInteger onCompletes; - private final CountDownLatch completeLatch; - private final AtomicInteger onErrors; - private final AtomicInteger onNexts; - - public UnsafeRedirectSubscriber() { - onCompletes = new AtomicInteger(); - completeLatch = new CountDownLatch(1); - onErrors = new AtomicInteger(); - onNexts = new AtomicInteger(); - } - - @Override - public void onCompleted() { - onCompletes.incrementAndGet(); - completeLatch.countDown(); - } - - @Override - public void onError(Throwable e) { - onErrors.incrementAndGet(); - completeLatch.countDown(); - } - - @Override - public void onNext(HttpClientResponse response) { - onNexts.incrementAndGet(); - } - - public int getOnCompletes() { - return onCompletes.get(); - } - - public int getOnErrors() { - return onErrors.get(); - } - - public int getOnNexts() { - return onNexts.get(); - } - - public void waitForCompletion(int time, TimeUnit unit) throws InterruptedException { - completeLatch.await(time, unit); - } - } - - private static class Setup { - - private TestableRedirectHandler handler; - private UnsafeRedirectSubscriber subscriber; - - public TestableRedirectHandler getHandler() { - return handler; - } - - public UnsafeRedirectSubscriber getSubscriber() { - return subscriber; - } - - public Setup setup(HttpResponseStatus redirectStatus) throws InterruptedException { - DefaultHttpRequest nettyRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, ""); - HttpClientRequest request = new HttpClientRequest(nettyRequest); - DefaultHttpResponse nettyResponse = new DefaultHttpResponse(HttpVersion.HTTP_1_1, - HttpResponseStatus.TEMPORARY_REDIRECT); - final HttpClientResponse response = - new HttpClientResponse(nettyResponse, - UnicastContentSubject .createWithoutNoSubscriptionTimeout()); - handler = new TestableRedirectHandler(2, redirectStatus); - - subscriber = new UnsafeRedirectSubscriber(); - Observable.just(response) - .lift(new RedirectOperator(request, handler)) - .unsafeSubscribe(subscriber); - - subscriber.waitForCompletion(1, TimeUnit.MINUTES); - return this; - } - } -*/ -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventPublisherTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventPublisherTest.java deleted file mode 100644 index d305c15..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventPublisherTest.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client.events; - -import io.reactivex.netty.protocol.http.client.events.HttpClientEventsListenerImpl.HttpEvent; -import io.reactivex.netty.test.util.MockClientEventListener.ClientEvent; -import io.reactivex.netty.test.util.MockConnectionEventListener.Event; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import static java.util.concurrent.TimeUnit.*; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class HttpClientEventPublisherTest { - - @Rule - public final PublisherRule rule = new PublisherRule(); - - @Test(timeout = 60000) - public void testOnRequestSubmitted() throws Exception { - rule.publisher.onRequestSubmitted(); - rule.listener.assertMethodCalled(HttpEvent.ReqSubmitted); - } - - @Test(timeout = 60000) - public void testOnRequestWriteStart() throws Exception { - rule.publisher.onRequestWriteStart(); - rule.listener.assertMethodCalled(HttpEvent.ReqWriteStart); - } - - @Test(timeout = 60000) - public void testOnRequestWriteComplete() throws Exception { - rule.publisher.onRequestWriteComplete(1, MILLISECONDS); - rule.listener.assertMethodCalled(HttpEvent.ReqWriteSuccess); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testOnRequestWriteFailed() throws Exception { - final Throwable expected = new NullPointerException(); - - rule.publisher.onRequestWriteFailed(1, MILLISECONDS, expected); - rule.listener.assertMethodCalled(HttpEvent.ReqWriteFailed); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(MILLISECONDS)); - assertThat("Listener not called with error.", rule.listener.getRecievedError(), is(expected)); - } - - @Test(timeout = 60000) - public void testOnResponseHeadersReceived() throws Exception { - rule.publisher.onResponseHeadersReceived(200, 1, MILLISECONDS); - rule.listener.assertMethodCalled(HttpEvent.ResHeadersReceived); - - assertThat("Listener not called with response code.", rule.listener.getResponseCode(), is(200)); - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testOnResponseContentReceived() throws Exception { - rule.publisher.onResponseContentReceived(); - rule.listener.assertMethodCalled(HttpEvent.ResContentReceived); - } - - @Test(timeout = 60000) - public void testOnResponseReceiveComplete() throws Exception { - rule.publisher.onResponseReceiveComplete(1, MILLISECONDS); - rule.listener.assertMethodCalled(HttpEvent.ResReceiveComplete); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testOnResponseFailed() throws Exception { - final Throwable expected = new NullPointerException(); - rule.publisher.onResponseFailed(expected); - rule.listener.assertMethodCalled(HttpEvent.RespFailed); - - assertThat("Listener not called with error.", rule.listener.getRecievedError(), is(expected)); - } - - @Test(timeout = 60000) - public void testOnRequestProcessingComplete() throws Exception { - rule.publisher.onRequestProcessingComplete(1, MILLISECONDS); - rule.listener.assertMethodCalled(HttpEvent.ProcessingComplete); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testOnConnectionCloseFailed() throws Exception { - final Throwable expected = new NullPointerException(); - rule.publisher.onConnectionCloseFailed(1, MILLISECONDS, expected); - - rule.listener.getTcpDelegate().assertMethodsCalled(Event.CloseFailed); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnConnectStart() throws Exception { - rule.publisher.onConnectStart(); - - rule.listener.getTcpDelegate().assertMethodsCalled(ClientEvent.ConnectStart); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnConnectSuccess() throws Exception { - rule.publisher.onConnectSuccess(1, MILLISECONDS); - - rule.listener.getTcpDelegate().assertMethodsCalled(ClientEvent.ConnectSuccess); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnConnectFailed() throws Exception { - rule.publisher.onConnectFailed(1, MILLISECONDS, new NullPointerException()); - - rule.listener.getTcpDelegate().assertMethodsCalled(ClientEvent.ConnectFailed); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnPoolReleaseStart() throws Exception { - rule.publisher.onPoolReleaseStart(); - - rule.listener.getTcpDelegate().assertMethodsCalled(ClientEvent.ReleaseStart); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnPoolReleaseSuccess() throws Exception { - rule.publisher.onPoolReleaseSuccess(1, MILLISECONDS); - - rule.listener.getTcpDelegate().assertMethodsCalled(ClientEvent.ReleaseSuccess); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnPoolReleaseFailed() throws Exception { - rule.publisher.onPoolReleaseFailed(1, MILLISECONDS, new NullPointerException()); - - rule.listener.getTcpDelegate().assertMethodsCalled(ClientEvent.ReleaseFailed); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnPooledConnectionEviction() throws Exception { - rule.publisher.onPooledConnectionEviction(); - - rule.listener.getTcpDelegate().assertMethodsCalled(ClientEvent.Eviction); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnPooledConnectionReuse() throws Exception { - rule.publisher.onPooledConnectionReuse(); - - rule.listener.getTcpDelegate().assertMethodsCalled(ClientEvent.Reuse); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnPoolAcquireStart() throws Exception { - rule.publisher.onPoolAcquireStart(); - - rule.listener.getTcpDelegate().assertMethodsCalled(ClientEvent.AcquireStart); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnPoolAcquireSuccess() throws Exception { - rule.publisher.onPoolAcquireSuccess(1, MILLISECONDS); - - rule.listener.getTcpDelegate().assertMethodsCalled(ClientEvent.AcquireSuccess); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnPoolAcquireFailed() throws Exception { - rule.publisher.onPoolAcquireFailed(1, MILLISECONDS, new NullPointerException()); - - rule.listener.getTcpDelegate().assertMethodsCalled(ClientEvent.AcquireFailed); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnByteRead() throws Exception { - rule.publisher.onByteRead(1); - - rule.listener.getTcpDelegate().assertMethodsCalled(Event.BytesRead); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnByteWritten() throws Exception { - rule.publisher.onByteWritten(1); - - rule.listener.getTcpDelegate().assertMethodsCalled(Event.BytesWritten); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnFlushStart() throws Exception { - rule.publisher.onFlushStart(); - - rule.listener.getTcpDelegate().assertMethodsCalled(Event.FlushStart); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnFlushSuccess() throws Exception { - rule.publisher.onFlushComplete(1, MILLISECONDS); - - rule.listener.getTcpDelegate().assertMethodsCalled(Event.FlushSuccess); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnWriteStart() throws Exception { - rule.publisher.onWriteStart(); - - rule.listener.getTcpDelegate().assertMethodsCalled(Event.WriteStart); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnWriteSuccess() throws Exception { - rule.publisher.onWriteSuccess(1, MILLISECONDS); - - rule.listener.getTcpDelegate().assertMethodsCalled(Event.WriteSuccess); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnWriteFailed() throws Exception { - rule.publisher.onWriteFailed(1, MILLISECONDS, new NullPointerException()); - - rule.listener.getTcpDelegate().assertMethodsCalled(Event.WriteFailed); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnConnectionCloseStart() throws Exception { - rule.publisher.onConnectionCloseStart(); - - rule.listener.getTcpDelegate().assertMethodsCalled(Event.CloseStart); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnConnectionCloseSuccess() throws Exception { - rule.publisher.onConnectionCloseSuccess(1, MILLISECONDS); - - rule.listener.getTcpDelegate().assertMethodsCalled(Event.CloseSuccess); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnCustomEvent() throws Exception { - rule.publisher.onCustomEvent("Hello"); - rule.listener.getTcpDelegate().assertMethodsCalled(Event.CustomEvent); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnCustomEventWithError() throws Exception { - rule.publisher.onCustomEvent("Hello", new NullPointerException()); - rule.listener.getTcpDelegate().assertMethodsCalled(Event.CustomEventWithError); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnCustomEventWithDuration() throws Exception { - rule.publisher.onCustomEvent("Hello", 1, MINUTES); - rule.listener.getTcpDelegate().assertMethodsCalled(Event.CustomEventWithDuration); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testOnCustomEventWithDurationAndError() throws Exception { - rule.publisher.onCustomEvent("Hello", 1, MINUTES, new NullPointerException()); - rule.listener.getTcpDelegate().assertMethodsCalled(Event.CustomEventWithDurationAndError); // Test for TCP should verify rest - } - - @Test(timeout = 60000) - public void testPublishingEnabled() throws Exception { - assertThat("Publishing not enabled.", rule.publisher.publishingEnabled(), is(true)); - } - - public static class PublisherRule extends ExternalResource { - - private HttpClientEventsListenerImpl listener; - private HttpClientEventPublisher publisher; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - listener = new HttpClientEventsListenerImpl(); - publisher = new HttpClientEventPublisher(); - publisher.subscribe(listener); - base.evaluate(); - } - }; - } - } - -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventsListenerImpl.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventsListenerImpl.java deleted file mode 100644 index 71af502..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventsListenerImpl.java +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client.events; - -import io.reactivex.netty.test.util.MockClientEventListener; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class HttpClientEventsListenerImpl extends HttpClientEventsListener { - - public enum HttpEvent { - ReqSubmitted, ReqWriteStart, ReqWriteSuccess, ReqWriteFailed, ResHeadersReceived, ResContentReceived, - ResReceiveComplete, RespFailed, ProcessingComplete - } - - private final MockClientEventListener tcpDelegate; - - private int responseCode; - private long duration; - private TimeUnit timeUnit; - private Throwable recievedError; - private final List methodsCalled = new ArrayList<>(); - - public HttpClientEventsListenerImpl() { - tcpDelegate = new MockClientEventListener(); - } - - @Override - public void onRequestSubmitted() { - methodsCalled.add(HttpEvent.ReqSubmitted); - } - - @Override - public void onRequestWriteStart() { - methodsCalled.add(HttpEvent.ReqWriteStart); - } - - @Override - public void onRequestWriteComplete(long duration, TimeUnit timeUnit) { - this.duration = duration; - this.timeUnit = timeUnit; - methodsCalled.add(HttpEvent.ReqWriteSuccess); - } - - @Override - public void onRequestWriteFailed(long duration, TimeUnit timeUnit, Throwable recievedError) { - this.duration = duration; - this.timeUnit = timeUnit; - this.recievedError = recievedError; - methodsCalled.add(HttpEvent.ReqWriteFailed); - } - - @Override - public void onResponseHeadersReceived(int responseCode, long duration, TimeUnit timeUnit) { - this.responseCode = responseCode; - this.duration = duration; - this.timeUnit = timeUnit; - methodsCalled.add(HttpEvent.ResHeadersReceived); - } - - @Override - public void onResponseContentReceived() { - methodsCalled.add(HttpEvent.ResContentReceived); - } - - @Override - public void onResponseReceiveComplete(long duration, TimeUnit timeUnit) { - this.duration = duration; - this.timeUnit = timeUnit; - methodsCalled.add(HttpEvent.ResReceiveComplete); - } - - @Override - public void onResponseFailed(Throwable recievedError) { - this.recievedError = recievedError; - methodsCalled.add(HttpEvent.RespFailed); - } - - @Override - public void onRequestProcessingComplete(long duration, TimeUnit timeUnit) { - this.duration = duration; - this.timeUnit = timeUnit; - methodsCalled.add(HttpEvent.ProcessingComplete); - } - - public int getResponseCode() { - return responseCode; - } - - public Throwable getRecievedError() { - return recievedError; - } - - public TimeUnit getTimeUnit() { - return timeUnit; - } - - public long getDuration() { - return duration; - } - - @Override - public void onConnectStart() { - tcpDelegate.onConnectStart(); - } - - @Override - public void onConnectSuccess(long duration, TimeUnit timeUnit) { - tcpDelegate.onConnectSuccess(duration, timeUnit); - } - - @Override - public void onConnectFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - tcpDelegate.onConnectFailed(duration, timeUnit, throwable); - } - - @Override - public void onPoolReleaseStart() { - tcpDelegate.onPoolReleaseStart(); - } - - @Override - public void onPoolReleaseSuccess(long duration, TimeUnit timeUnit) { - tcpDelegate.onPoolReleaseSuccess(duration, timeUnit); - } - - @Override - public void onPoolReleaseFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - tcpDelegate.onPoolReleaseFailed(duration, timeUnit, throwable); - } - - @Override - public void onPooledConnectionEviction() { - tcpDelegate.onPooledConnectionEviction(); - } - - @Override - public void onPooledConnectionReuse() { - tcpDelegate.onPooledConnectionReuse(); - } - - @Override - public void onPoolAcquireStart() { - tcpDelegate.onPoolAcquireStart(); - } - - @Override - public void onPoolAcquireSuccess(long duration, TimeUnit timeUnit) { - tcpDelegate.onPoolAcquireSuccess(duration, timeUnit); - } - - @Override - public void onPoolAcquireFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - tcpDelegate.onPoolAcquireFailed(duration, timeUnit, throwable); - } - - @Override - public void onByteRead(long bytesRead) { - tcpDelegate.onByteRead(bytesRead); - } - - @Override - public void onByteWritten(long bytesWritten) { - tcpDelegate.onByteWritten(bytesWritten); - } - - @Override - public void onFlushStart() { - tcpDelegate.onFlushStart(); - } - - @Override - public void onFlushComplete(long duration, TimeUnit timeUnit) { - tcpDelegate.onFlushComplete(duration, timeUnit); - } - - @Override - public void onWriteStart() { - tcpDelegate.onWriteStart(); - } - - @Override - public void onWriteSuccess(long duration, TimeUnit timeUnit) { - tcpDelegate.onWriteSuccess(duration, timeUnit); - } - - @Override - public void onWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - tcpDelegate.onWriteFailed(duration, timeUnit, throwable); - } - - @Override - public void onConnectionCloseStart() { - tcpDelegate.onConnectionCloseStart(); - } - - @Override - public void onConnectionCloseSuccess(long duration, TimeUnit timeUnit) { - tcpDelegate.onConnectionCloseSuccess(duration, timeUnit); - } - - @Override - public void onConnectionCloseFailed(long duration, TimeUnit timeUnit, - Throwable throwable) { - tcpDelegate.onConnectionCloseFailed(duration, timeUnit, throwable); - } - - @Override - public void onCustomEvent(Object event) { - tcpDelegate.onCustomEvent(event); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { - tcpDelegate.onCustomEvent(event, duration, timeUnit); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { - tcpDelegate.onCustomEvent(event, duration, timeUnit, throwable); - } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { - tcpDelegate.onCustomEvent(event, throwable); - } - - @Override - public void onCompleted() { - tcpDelegate.onCompleted(); - } - - public void assertMethodCalled(HttpEvent... events) { - assertThat("Unexpected methods called count.", methodsCalled, hasSize(events.length)); - assertThat("Unexpected methods called.", methodsCalled, contains(events)); - } - - public MockClientEventListener getTcpDelegate() { - return tcpDelegate; - } - - @Override - public String toString() { - return "HttpClientEventsListenerImpl{" + - "tcpDelegate=" + tcpDelegate + - ", responseCode=" + responseCode + - ", duration=" + duration + - ", timeUnit=" + timeUnit + - ", recievedError=" + recievedError + - ", methodsCalled=" + methodsCalled + - '}'; - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventsTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventsTest.java deleted file mode 100644 index 95f7427..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/events/HttpClientEventsTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.reactivex.netty.protocol.http.client.events; - -import io.netty.buffer.ByteBuf; -import io.reactivex.netty.client.Host; -import io.reactivex.netty.client.pool.SingleHostPoolingProviderFactory; -import io.reactivex.netty.protocol.http.client.HttpClient; -import io.reactivex.netty.protocol.http.client.HttpClientRequest; -import io.reactivex.netty.protocol.http.client.HttpClientResponse; -import io.reactivex.netty.protocol.http.server.HttpServerRule; -import io.reactivex.netty.test.util.MockClientEventListener.ClientEvent; -import io.reactivex.netty.test.util.MockConnectionEventListener.Event; -import org.junit.Rule; -import org.junit.Test; -import rx.Observable; - -public class HttpClientEventsTest { - - @Rule - public final HttpServerRule serverRule = new HttpServerRule(); - - @Test(timeout = 60000) - public void testEventsPublished() throws Exception { - HttpClientEventsListenerImpl listener = sendRequests(false); - - listener.getTcpDelegate().assertMethodCalled(ClientEvent.ConnectStart); - listener.getTcpDelegate().assertMethodCalled(ClientEvent.ConnectSuccess); - listener.getTcpDelegate().assertMethodCalled(Event.WriteStart); - listener.getTcpDelegate().assertMethodCalled(Event.WriteSuccess); - listener.getTcpDelegate().assertMethodCalled(Event.FlushStart); - listener.getTcpDelegate().assertMethodCalled(Event.FlushSuccess); - listener.getTcpDelegate().assertMethodCalled(Event.BytesRead); - } - - @Test(timeout = 60000) - public void testPooledEventsPublished() throws Exception { - HttpClientEventsListenerImpl listener = sendRequests(true); - - listener.getTcpDelegate().assertMethodCalled(ClientEvent.AcquireStart); - listener.getTcpDelegate().assertMethodCalled(ClientEvent.AcquireSuccess); - listener.getTcpDelegate().assertMethodCalled(ClientEvent.ConnectStart); - listener.getTcpDelegate().assertMethodCalled(ClientEvent.ConnectSuccess); - listener.getTcpDelegate().assertMethodCalled(Event.WriteStart); - listener.getTcpDelegate().assertMethodCalled(Event.WriteSuccess); - listener.getTcpDelegate().assertMethodCalled(Event.FlushStart); - listener.getTcpDelegate().assertMethodCalled(Event.FlushSuccess); - listener.getTcpDelegate().assertMethodCalled(Event.BytesRead); - } - - protected HttpClientEventsListenerImpl sendRequests(boolean pool) { - serverRule.startServer(); - HttpClientEventsListenerImpl listener = new HttpClientEventsListenerImpl(); - if (pool) { - SingleHostPoolingProviderFactory provider = - SingleHostPoolingProviderFactory.createBounded(10); - Host host = new Host(serverRule.getServerAddress()); - serverRule.setupClient(HttpClient.newClient(provider, Observable.just(host))); - - } - serverRule.getClient().subscribe(listener); - HttpClientRequest request = serverRule.getClient().createGet("/"); - - HttpClientResponse resp = serverRule.sendRequest(request); - serverRule.assertResponseContent(resp); - return listener; - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/internal/HttpClientRequestImplTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/internal/HttpClientRequestImplTest.java deleted file mode 100644 index e7d113e..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/client/internal/HttpClientRequestImplTest.java +++ /dev/null @@ -1,940 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.client.internal; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.cookie.ClientCookieEncoder; -import io.netty.handler.codec.http.cookie.DefaultCookie; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.util.concurrent.EventExecutorGroup; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.ConnectionImpl; -import io.reactivex.netty.channel.ConnectionInputSubscriberEvent; -import io.reactivex.netty.events.EventAttributeKeys; -import io.reactivex.netty.protocol.http.TrailingHeaders; -import io.reactivex.netty.protocol.http.client.HttpClientResponse; -import io.reactivex.netty.protocol.tcp.client.TcpClient; -import io.reactivex.netty.protocol.tcp.client.events.TcpClientEventPublisher; -import io.reactivex.netty.test.util.MockEventPublisher; -import io.reactivex.netty.test.util.FlushSelector; -import io.reactivex.netty.test.util.TcpConnectionRequestMock; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.mockito.Matchers; -import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import rx.Observable; -import rx.Observer; -import rx.Subscriber; -import rx.functions.Action1; -import rx.functions.Func0; -import rx.functions.Func1; -import rx.functions.Func2; -import rx.observers.TestSubscriber; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map.Entry; -import java.util.TimeZone; -import java.util.concurrent.atomic.AtomicInteger; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; -import static io.netty.handler.codec.http.HttpHeaderValues.*; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.*; -import static org.mockito.Matchers.*; - -public class HttpClientRequestImplTest { - - @Rule - public final RequestRule requestRule = new RequestRule(); - - @Test(timeout = 60000) - public void testWriteContent() throws Exception { - Observable content = Observable.just("Hello"); - Observable> newReq = requestRule.request.writeContent(content); - - requestRule.assertContentWrite(content, newReq); - } - - @Test(timeout = 60000) - public void testWriteContentAndFlushOnEach() throws Exception { - Observable content = Observable.just("Hello"); - Observable> newReq = requestRule.request.writeContentAndFlushOnEach(content); - - requestRule.assertContentWriteAndFlushOnEach(content, newReq); - } - - @Test(timeout = 60000) - public void testWriteStringContent() throws Exception { - Observable content = Observable.just("Hello"); - Observable> newReq = requestRule.request.writeStringContent(content); - requestRule.assertContentWrite(content, newReq); - } - - @Test(timeout = 60000) - public void testWriteBytesContent() throws Exception { - Observable content = Observable.just("Hello".getBytes()); - Observable> newReq = requestRule.request.writeBytesContent(content); - requestRule.assertContentWrite(content, newReq); - } - - @Test(timeout = 60000) - public void testWriteContentWithFlushSelector() throws Exception { - Observable content = Observable.just("Hello"); - FlushSelector flushSelector = new FlushSelector<>(5); - Observable> newReq = requestRule.request.writeContent(content, flushSelector); - - requestRule.assertContentWrite(content, newReq, flushSelector); - } - - @Test(timeout = 60000) - public void testWriteStringContentWithFlushSelector() throws Exception { - Observable content = Observable.just("Hello"); - FlushSelector flushSelector = new FlushSelector<>(5); - Observable> newReq = requestRule.request.writeStringContent(content, flushSelector); - - requestRule.assertContentWrite(content, newReq, flushSelector); - } - - @Test(timeout = 60000) - public void testWriteBytesContentWithFlushSelector() throws Exception { - Observable content = Observable.just("Hello".getBytes()); - FlushSelector flushSelector = new FlushSelector<>(5); - Observable> newReq = requestRule.request.writeBytesContent(content, flushSelector); - - requestRule.assertContentWrite(content, newReq, flushSelector); - } - - @Test(timeout = 60000) - public void testWriteContentWithTrailer() throws Exception { - Observable content = Observable.just("Hello"); - TestTrailerFactory tFactory = requestRule.newTrailerFactory(); - TestTrailerMutator tMutator = requestRule.newTrailerMutator(); - Observable> newReq = requestRule.request.writeContent(content, tFactory, tMutator); - - requestRule.assertContentWrite(content, newReq, tFactory, tMutator); - } - - @Test(timeout = 60000) - public void testWriteStringContentWithTrailer() throws Exception { - Observable content = Observable.just("Hello"); - TestTrailerFactory tFactory = requestRule.newTrailerFactory(); - TestTrailerMutator tMutator = requestRule.newTrailerMutator(); - Observable> newReq = requestRule.request.writeStringContent(content, tFactory, - tMutator); - - requestRule.assertContentWrite(content, newReq, tFactory, tMutator); - } - - @Test(timeout = 60000) - public void testWriteBytesContentWithTrailer() throws Exception { - Observable content = Observable.just("Hello".getBytes()); - TestTrailerFactory tFactory = requestRule.newTrailerFactory(); - TestTrailerMutator tMutator = requestRule.newTrailerMutator(); - Observable> newReq = requestRule.request.writeBytesContent(content, tFactory, - tMutator); - - requestRule.assertContentWrite(content, newReq, tFactory, tMutator); - } - - @Test(timeout = 60000) - public void testWriteContentWithTrailerAndSelector() throws Exception { - Observable content = Observable.just("Hello".getBytes()); - FlushSelector selector = new FlushSelector<>(1); - TestTrailerFactory tFactory = requestRule.newTrailerFactory(); - TestTrailerMutator tMutator = requestRule.newTrailerMutator(); - Observable> newReq = requestRule.request.writeContent(content, tFactory, tMutator, - selector); - - requestRule.assertContentWrite(content, newReq, tFactory, tMutator, 2/*One for content & one for trailer*/); - } - - @Test(timeout = 60000) - public void testWriteStringContentWithTrailerAndSelector() throws Exception { - Observable content = Observable.just("Hello"); - FlushSelector selector = new FlushSelector<>(1); - TestTrailerFactory tFactory = requestRule.newTrailerFactory(); - TestTrailerMutator tMutator = requestRule.newTrailerMutator(); - Observable> newReq = requestRule.request.writeStringContent(content, tFactory, - tMutator, selector); - - requestRule.assertContentWrite(content, newReq, tFactory, tMutator, 2/*One for content & one for trailer*/); - } - - @Test(timeout = 60000) - public void testWriteBytesContentWithTrailerAndSelector() throws Exception { - Observable content = Observable.just("Hello".getBytes()); - FlushSelector selector = new FlushSelector<>(1); - TestTrailerFactory tFactory = requestRule.newTrailerFactory(); - TestTrailerMutator tMutator = requestRule.newTrailerMutator(); - Observable> newReq = requestRule.request.writeBytesContent(content, tFactory, - tMutator, selector); - - requestRule.assertContentWrite(content, newReq, tFactory, tMutator, 2/*One for content & one for trailer*/); - } - - @Test(timeout = 60000) - public void testAddHeader() throws Exception { - final String headerName = "Foo"; - final String headerVal = "bar"; - - HttpClientRequestImpl newReq = - requestRule.request.addHeader(headerName, headerVal); - - requestRule.assertCopy(newReq); - - requestRule.assertHeaderAdded(newReq, headerName, headerVal); - } - - @Test(timeout = 60000) - public void testAddCookie() throws Exception { - DefaultCookie cookie = new DefaultCookie("cookie", "cook"); - HttpClientRequestImpl newReq = requestRule.request.addCookie(cookie); - - requestRule.assertCopy(newReq); - - requestRule.assertHeaderAdded(newReq, COOKIE.toString(), ClientCookieEncoder.STRICT.encode(cookie)); - } - - @Test(timeout = 60000) - public void testAddDateHeader() throws Exception { - String headerName = "date"; - Date date = new Date(); - - HttpClientRequestImpl newReq = requestRule.request.addDateHeader(headerName, date); - - requestRule.assertCopy(newReq); - - requestRule.assertHeaderAdded(newReq, headerName, date); - } - - @Test(timeout = 60000) - public void testAddDateHeaderMulti() throws Exception { - String headerName = "date"; - Date date1 = new Date(); - Date date2 = new Date(); - - HttpClientRequestImpl newReq = requestRule.request.addDateHeader(headerName, - Arrays.asList(date1, date2)); - - requestRule.assertCopy(newReq); - - requestRule.assertHeaderAdded(newReq, headerName, date1, date2); - } - - @Test(timeout = 60000) - public void testAddDateHeaderIncrementally() throws Exception { - String headerName = "foo"; - Date date1 = new Date(); - Date date2 = new Date(); - - HttpClientRequestImpl newReq = requestRule.request.addHeader(headerName, date1); - - requestRule.assertCopy(newReq); - - requestRule.assertHeaderAdded(newReq, headerName, date1); - - HttpClientRequestImpl newReq2 = newReq.addHeader(headerName, date2); - - requestRule.assertCopy(newReq, newReq2); - - requestRule.assertHeaderAdded(newReq2, headerName, date1, date2); - } - - @Test(timeout = 60000) - public void testAddHeaderMulti() throws Exception { - String headerName = "foo"; - String val1 = "val1"; - String val2 = "val2"; - - HttpClientRequestImpl newReq = - requestRule.request.addHeaderValues(headerName, Arrays.asList(val1, val2)); - - requestRule.assertCopy(newReq); - - requestRule.assertHeaderAdded(newReq, headerName, val1, val2); - } - - @Test(timeout = 60000) - public void testAddHeaderIncrementally() throws Exception { - String headerName = "foo"; - String val1 = "val1"; - String val2 = "val2"; - - HttpClientRequestImpl newReq = - requestRule.request.addHeader(headerName, val1); - - requestRule.assertCopy(newReq); - - requestRule.assertHeaderAdded(newReq, headerName, val1); - - HttpClientRequestImpl newReq2 = newReq.addHeader(headerName, val2); - - requestRule.assertCopy(newReq, newReq2); - - requestRule.assertHeaderAdded(newReq2, headerName, val1, val2); - } - - @Test(timeout = 60000) - public void testSetDateHeader() { - String headerName = "date"; - Date date1 = new Date(); - - HttpClientRequestImpl addReq = requestRule.request.addDateHeader(headerName, date1); - - requestRule.assertCopy(addReq); - - requestRule.assertHeaderAdded(addReq, headerName, date1); - - Date date2 = new Date(100); - HttpClientRequestImpl setReq = requestRule.request.setDateHeader(headerName, date2); - - requestRule.assertCopy(setReq); - - requestRule.assertHeaderAdded(setReq, headerName, date2); - } - - @Test(timeout = 60000) - public void testSetHeader() throws Exception { - String headerName = "foo"; - String val1 = "bar"; - - HttpClientRequestImpl addReq = requestRule.request.addHeader(headerName, val1); - - requestRule.assertCopy(addReq); - - requestRule.assertHeaderAdded(addReq, headerName, val1); - - String val2 = "bar2"; - HttpClientRequestImpl setReq = requestRule.request.setHeader(headerName, val2); - - requestRule.assertCopy(setReq); - - requestRule.assertHeaderAdded(setReq, headerName, val2); - } - - @Test(timeout = 60000) - public void testSetDateHeaderMulti() throws Exception { - String headerName = "date"; - Date date1 = new Date(); - - HttpClientRequestImpl addReq = requestRule.request.addDateHeader(headerName, date1); - - requestRule.assertCopy(addReq); - - requestRule.assertHeaderAdded(addReq, headerName, date1); - - Date date2 = new Date(100); - Date date3 = new Date(500); - - HttpClientRequestImpl setReq = requestRule.request.setDateHeader(headerName, - Arrays.asList(date2, date3)); - - requestRule.assertCopy(setReq); - - requestRule.assertHeaderAdded(setReq, headerName, date2, date3); - } - - @Test(timeout = 60000) - public void testSetHeaderMulti() throws Exception { - String headerName = "date"; - Date date1 = new Date(); - - HttpClientRequestImpl addReq = requestRule.request.addDateHeader(headerName, date1); - - requestRule.assertCopy(addReq); - - requestRule.assertHeaderAdded(addReq, headerName, date1); - - String val2 = "bar2"; - String val3 = "bar3"; - - HttpClientRequestImpl setReq = requestRule.request.setHeaderValues(headerName, - Arrays.asList(val2, val3)); - - requestRule.assertCopy(setReq); - - requestRule.assertHeaderAdded(setReq, headerName, val2, val3); - } - - @Test(timeout = 60000) - public void testRemoveHeader() throws Exception { - final String headerName = "Foo"; - final String headerVal = "bar"; - - HttpClientRequestImpl newReq = requestRule.request.addHeader(headerName, headerVal); - - requestRule.assertCopy(newReq); - - requestRule.assertHeaderAdded(newReq, headerName, headerVal); - - HttpClientRequestImpl newReq2 = newReq.removeHeader(headerName); - - requestRule.assertCopy(newReq2, newReq); - - HttpRequest newReqHeaders = newReq2.unsafeRawRequest().getHeaders(); - HttpRequest origReqHeaders = newReq.unsafeRawRequest().getHeaders(); - - assertThat("Header not removed.", newReqHeaders.headers().contains(headerName), is(false)); - assertThat("Header removed from original request.", origReqHeaders.headers().contains(headerName), - is(true)); - } - - @Test(timeout = 60000) - public void testSetKeepAlive() throws Exception { - HttpClientRequestImpl newReq = requestRule.request.setKeepAlive(false); - - requestRule.assertHeaderAdded(newReq, CONNECTION.toString(), CLOSE.toString()); - } - - @Test(timeout = 60000) - public void testSetTransferEncodingChunked() throws Exception { - HttpClientRequestImpl newReq = requestRule.request.setTransferEncodingChunked(); - - requestRule.assertHeaderAdded(newReq, TRANSFER_ENCODING.toString(), CHUNKED.toString()); - - } - - @Test(timeout = 60000) - public void testContainsHeader() throws Exception { - final String headerName = "Foo"; - final String headerVal = "bar"; - - HttpClientRequestImpl newReq = requestRule.request.addHeader(headerName, headerVal); - - requestRule.assertHeaderAdded(newReq, headerName, headerVal); - - assertThat("Added header not retrievable.", newReq.containsHeader(headerName), is(true)); - } - - @Test(timeout = 60000) - public void testContainsHeaderWithValue() throws Exception { - final String headerName = "Foo"; - final String headerVal1 = "bar"; - final String headerVal2 = "bar2"; - - HttpClientRequestImpl newReq = requestRule.request - .addHeaderValues(headerName, Arrays.asList(headerVal1, headerVal2)); - - requestRule.assertHeaderAdded(newReq, headerName, headerVal1, headerVal2); - - assertThat("Added header not retrievable.", newReq.containsHeaderWithValue(headerName, headerVal1, false), - is(true)); - } - - @Test(timeout = 60000) - public void testContainsHeaderWithValueCaseInsensitive() throws Exception { - final String headerName = "Foo"; - final String headerVal = "bar"; - - HttpClientRequestImpl newReq = requestRule.request.addHeader(headerName, headerVal); - - requestRule.assertHeaderAdded(newReq, headerName, headerVal); - - assertThat("Added header not retrievable.", newReq.containsHeaderWithValue(headerName, "BaR", true), - is(true)); - } - - @Test(timeout = 60000) - public void testGetHeader() throws Exception { - final String headerName = "Foo"; - final String headerVal = "bar"; - - HttpClientRequestImpl newReq = requestRule.request.addHeader(headerName, headerVal); - - requestRule.assertHeaderAdded(newReq, headerName, headerVal); - - assertThat("Added header not retrievable.", newReq.getHeader(headerName), is(headerVal)); - } - - @Test(timeout = 60000) - public void testGetAllHeaders() throws Exception { - final String headerName = "Foo"; - final String headerVal1 = "bar"; - final String headerVal2 = "bar2"; - - HttpClientRequestImpl newReq = requestRule.request - .addHeaderValues(headerName, Arrays.asList(headerVal1, headerVal2)); - - requestRule.assertHeaderAdded(newReq, headerName, headerVal1, headerVal2); - - assertThat("Added header not retrievable.", newReq.getAllHeaders(headerName), - hasSize(2)); - - assertThat("Added header not retrievable.", newReq.getAllHeaders(headerName), - contains(headerVal1, headerVal2)); - } - - @Test(timeout = 60000) - public void testGetHttpVersion() throws Exception { - assertThat("Unexpected http version", requestRule.request.getHttpVersion(), is(HttpVersion.HTTP_1_1)); - } - - @Test(timeout = 60000) - public void testGetMethod() throws Exception { - assertThat("Unexpected http version", requestRule.request.getMethod(), is(HttpMethod.GET)); - } - - @Test(timeout = 60000) - public void testGetUri() throws Exception { - assertThat("Unexpected http version", requestRule.request.getUri(), is("/")); - } - - @Test(timeout = 60000) - public void testHeaderIterator() throws Exception { - final String headerName = "Foo"; - final String headerVal1 = "bar"; - - HttpClientRequestImpl newReq = - requestRule.request.addHeader(headerName, headerVal1); - - requestRule.assertHeaderAdded(newReq, headerName, headerVal1); - - Iterator> headerIter = newReq.headerIterator(); - List> allHeaders = new ArrayList<>(); - while (headerIter.hasNext()) { - Entry next = headerIter.next(); - allHeaders.add(next); - } - - assertThat("Added header not retrievable.", allHeaders, hasSize(1)); - assertThat("Unexpected header name.", allHeaders.get(0).getKey(), equalTo((CharSequence)headerName)); - assertThat("Unexpected header value.", allHeaders.get(0).getValue(), equalTo((CharSequence)headerVal1)); - } - - @Test(timeout = 60000) - public void testGetHeaderNames() throws Exception { - final String headerName = "Foo"; - final String headerVal1 = "bar"; - - HttpClientRequestImpl newReq = requestRule.request.addHeader(headerName, headerVal1); - - requestRule.assertHeaderAdded(newReq, headerName, headerVal1); - - assertThat("Added header not retrievable.", newReq.getHeaderNames(), hasSize(1)); - assertThat("Unexpected header name.", newReq.getHeaderNames(), contains(headerName)); - } - - @Test(timeout = 60000) - public void testSubscribe() throws Exception { - TestSubscriber subscriber = new TestSubscriber<>(); - - Observable> newReq = requestRule.request.writeStringContent(Observable.just("Hello")); - RawRequest rawReq = RequestRule.getRawRequest(newReq); - - newReq.subscribe(subscriber); - - subscriber.assertNoErrors(); - requestRule.channel.flush(); /*Since nobody subscribes to the observable.*/ - - assertThat("Unexpected number of items written on the channel.", requestRule.channel.outboundMessages(), - hasSize(1)); - Object outboundMsg = requestRule.channel.readOutbound(); - assertThat("Unexpected item written on the channel.", outboundMsg, instanceOf(Observable.class)); - - @SuppressWarnings("unchecked") - Observable writtenO = (Observable) outboundMsg; - TestSubscriber writtenOSub = new TestSubscriber<>(); - writtenO.subscribe(writtenOSub); - - writtenOSub.assertTerminalEvent(); - writtenOSub.assertNoErrors(); - - @SuppressWarnings("unchecked") - Observable rawReqO = (Observable) rawReq.asObservable(requestRule.connMock); - - TestSubscriber rawReqOSub = new TestSubscriber<>(); - rawReqO.subscribe(rawReqOSub); - - rawReqOSub.assertTerminalEvent(); - rawReqOSub.assertNoErrors(); - - assertThat("Unexpected items count in Observable written on channel.", writtenOSub.getOnNextEvents(), - hasSize(rawReqOSub.getOnNextEvents().size())); - assertThat("Unexpected items in Observable written on channel.", writtenOSub.getOnNextEvents(), - contains(rawReqOSub.getOnNextEvents().toArray())); - - DefaultFullHttpResponse nettyResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, - HttpResponseStatus.ACCEPTED); - HttpClientResponse response = HttpClientResponseImpl.newInstance(nettyResponse, requestRule.connMock); - requestRule.addToConnectionInput(response); - - subscriber.assertTerminalEvent(); - subscriber.assertNoErrors(); - - assertThat("Unexpected response count received.", subscriber.getOnNextEvents(), hasSize(1)); - assertThat("Unexpected response received.", subscriber.getOnNextEvents().get(0), - instanceOf(HttpClientResponse.class)); - @SuppressWarnings("unchecked") - HttpClientResponse actual = (HttpClientResponse) subscriber.getOnNextEvents().get(0); - - assertThat("Unexpected response received.", actual.getStatus(), is(HttpResponseStatus.ACCEPTED)); - assertThat("Unexpected response received.", actual.getHttpVersion(), is(HttpVersion.HTTP_1_1)); - } - - public static class RequestRule extends ExternalResource { - - private HttpClientRequestImpl request; - private TcpClient> clientMock; - private Connection> connMock; - private EmbeddedChannel channel; - @SuppressWarnings("rawtypes") - private Subscriber cis; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - @SuppressWarnings("unchecked") - TcpClient> clientMock = - (TcpClient>) Mockito.mock(TcpClient.class); - - channel = new EmbeddedChannel(new ChannelDuplexHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof ConnectionInputSubscriberEvent) { - @SuppressWarnings({"rawtypes", "unchecked"}) - ConnectionInputSubscriberEvent cise = (ConnectionInputSubscriberEvent) evt; - cis = cise.getSubscriber(); - } - super.userEventTriggered(ctx, evt); - } - }); - - TcpClientEventPublisher eventPublisher = new TcpClientEventPublisher(); - - channel.attr(EventAttributeKeys.EVENT_PUBLISHER).set(eventPublisher); - channel.attr(EventAttributeKeys.CLIENT_EVENT_LISTENER).set(eventPublisher); - channel.attr(EventAttributeKeys.CONNECTION_EVENT_LISTENER).set(eventPublisher); - - connMock = ConnectionImpl.fromChannel(channel); - - @SuppressWarnings("unchecked") - final - TcpConnectionRequestMock> connReqMock = - new TcpConnectionRequestMock(Observable.just(connMock)); - - Mockito.when(clientMock.createConnectionRequest()) - .thenAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - return connReqMock; - } - }); - - Answer returnThisMock = new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - return invocation.getMock(); - } - }; - - Mockito.when(clientMock.addChannelHandlerFirst(anyString(), - Matchers.>anyObject())) - .thenAnswer(returnThisMock); - - Mockito.when(clientMock.addChannelHandlerFirst(Matchers.anyObject(), - anyString(), - Matchers.>anyObject())) - .thenAnswer(returnThisMock); - - Mockito.when(clientMock.addChannelHandlerLast(anyString(), - Matchers.>anyObject())) - .thenAnswer(returnThisMock); - - Mockito.when(clientMock.addChannelHandlerLast(Matchers.anyObject(), - anyString(), - Matchers.>anyObject())) - .thenAnswer(returnThisMock); - - Mockito.when(clientMock.addChannelHandlerBefore(anyString(), anyString(), - Matchers.>anyObject())) - .thenAnswer(returnThisMock); - - Mockito.when(clientMock.addChannelHandlerBefore(Matchers.anyObject(), - anyString(), anyString(), - Matchers.>anyObject())) - .thenAnswer(returnThisMock); - - Mockito.when(clientMock.addChannelHandlerAfter(anyString(), anyString(), - Matchers.>anyObject())) - .thenAnswer(returnThisMock); - - Mockito.when(clientMock.addChannelHandlerAfter(Matchers.anyObject(), - anyString(), anyString(), - Matchers.>anyObject())) - .thenAnswer(returnThisMock); - - Mockito.when(clientMock.pipelineConfigurator(Matchers.>anyObject())) - .thenAnswer(returnThisMock); - - Mockito.when(clientMock.enableWireLogging(anyString(), Matchers.anyObject())) - .thenAnswer(returnThisMock); - - RequestRule.this.clientMock = clientMock; - - request = HttpClientRequestImpl.create(HttpVersion.HTTP_1_1, - HttpMethod.GET, "/", - RequestRule.this.clientMock - ); - base.evaluate(); - } - }; - } - - public void assertCopy(HttpClientRequestImpl newReq) { - assertCopy(request, newReq); - } - - public void assertCopy(HttpClientRequestImpl oldReq, - HttpClientRequestImpl newReq) { - assertThat("Request not copied.", newReq, not(equalTo(oldReq))); - assertThat("Underlying raw request not copied.", newReq.unsafeRawRequest(), - not(equalTo(oldReq.unsafeRawRequest()))); - assertThat("Underlying raw request headers not copied.", newReq.unsafeRawRequest().getHeaders(), - not(equalTo(oldReq.unsafeRawRequest().getHeaders()))); - } - - public void assertHeaderAdded(HttpClientRequestImpl newReq, String headerName, - String... headerVals) { - assertHeaderAdded(request, newReq, headerName, headerVals); - } - - public void assertHeaderAdded(HttpClientRequestImpl oldReq, - HttpClientRequestImpl newReq, String headerName, - String... headerVals) { - - HttpRequest newReqHeaders = newReq.unsafeRawRequest().getHeaders(); - HttpRequest origReqHeaders = oldReq.unsafeRawRequest().getHeaders(); - - assertThat("New header not added.", newReqHeaders.headers().contains(headerName), is(true)); - assertThat("Unexpected header value.", newReqHeaders.headers().getAll(headerName), contains(headerVals)); - assertThat("More than one header added.", newReqHeaders.headers().names(), hasSize(1)); - - assertThat("New header added to original request.", origReqHeaders.headers().names(), is(empty())); - } - - public void assertHeaderAdded(HttpClientRequestImpl newReq, String headerName, - Date... dates) { - SimpleDateFormat sdf = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z", Locale.US); - sdf.setTimeZone(TimeZone.getTimeZone("GMT")); - - String[] expectedValues = new String[dates.length]; - for (int i = 0; i < dates.length; i++) { - Date date = dates[i]; - expectedValues[i] = sdf.format(date); - } - - assertHeaderAdded(newReq, headerName, expectedValues); - } - - RawRequest assertContentWrite(@SuppressWarnings("rawtypes") Observable contentWritten, - Observable> newReq) { - RawRequest rawRequest = _assertContentWriteContentOnly(contentWritten, newReq); - assertThat("Unexpected flush selector in the created raw request.", rawRequest.getFlushSelector(), - is(nullValue())); - assertThat("Unexpected trailers flag in the created raw request.", rawRequest.hasTrailers(), - is(false)); - return rawRequest; - } - - RawRequest assertContentWriteAndFlushOnEach(@SuppressWarnings("rawtypes") Observable contentWritten, - Observable> newReq) { - - RawRequest rawRequest = _assertContentWriteContentOnly(contentWritten, newReq); - assertThat("Unexpected flush selector in the created raw request.", rawRequest.getFlushSelector(), - is(notNullValue())); - /*Just a way to assert that it is an unconditional flush on each*/ - assertThat("Unexpected flush selector implementation in the created raw request.", - rawRequest.getFlushSelector().call(null), is(true)); - assertThat("Unexpected trailers flag in the created raw request.", rawRequest.hasTrailers(), - is(false)); - return rawRequest; - } - - RawRequest assertContentWrite(@SuppressWarnings("rawtypes") Observable contentWritten, - Observable> newReq, - @SuppressWarnings("rawtypes") Func1 selector) { - - RawRequest rawRequest = _assertContentWriteContentOnly(contentWritten, newReq); - - @SuppressWarnings({"unchecked", "rawtypes"}) - Func1 selectorFound = rawRequest.getFlushSelector(); - assertThat("Unexpected flush selector in the created raw request.", selectorFound, - is(notNullValue())); - assertThat("Unexpected flush selector implementation in the created raw request.", - selectorFound, equalTo(selector)); - assertThat("Unexpected trailers flag in the created raw request.", rawRequest.hasTrailers(), - is(false)); - return rawRequest; - } - - public int assertContentWrite(Observable content, Observable> newReq, - TestTrailerFactory tFactory, TestTrailerMutator tMutator) { - RawRequest rawReq = getRawRequest(newReq); - - final AtomicInteger flushCount = new AtomicInteger(); - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()) { - @Override - public Channel flush() { - flushCount.incrementAndGet(); - return super.flush(); - } - }; - - channel.attr(EventAttributeKeys.EVENT_PUBLISHER).set(MockEventPublisher.disabled()); - ConnectionImpl conn = ConnectionImpl.fromChannel(channel); - - Observable reqAsO = rawReq.asObservable(conn); - - TestSubscriber writtenContentSub = new TestSubscriber<>(); - content.subscribe(writtenContentSub); - - writtenContentSub.assertTerminalEvent(); - writtenContentSub.assertNoErrors(); - - TestSubscriber reqSubscriber = new TestSubscriber<>(); - reqAsO.subscribe((Observer)reqSubscriber); - - reqSubscriber.awaitTerminalEvent(); - reqSubscriber.assertNoErrors(); - - @SuppressWarnings("unchecked") - List writtenOnNextEvents = (List) writtenContentSub.getOnNextEvents(); - - List reqOnNextEvents = reqSubscriber.getOnNextEvents(); - - assertThat("Unexpected items in raw request as Observable.", reqOnNextEvents, - hasSize(writtenOnNextEvents.size() + 2)); - - assertThat("Unexpected type of first item in raw request Observable.", reqOnNextEvents.get(0), - instanceOf(HttpRequest.class)); - - HttpRequest headers = (HttpRequest) reqOnNextEvents.get(0); - assertThat("Unexpected headers in the created raw request.", headers, - is(request.unsafeRawRequest().getHeaders())); - - assertThat("Unexpected type of last item in raw request Observable.", - reqOnNextEvents.get(reqOnNextEvents.size() - 1), - instanceOf(TrailingHeaders.class)); - - TrailingHeaders trailers = (TrailingHeaders) reqOnNextEvents.get(reqOnNextEvents.size() - 1); - assertThat("Unexpected trailing headers in the created raw request.", trailers, - is(tFactory.lastReturned)); - assertThat("Unexpected trailer mutator invocation count.", tMutator.callCount, - is(writtenOnNextEvents.size())); - - List contentItems = reqOnNextEvents.subList(1, reqOnNextEvents.size() - 1); - - assertThat("Unexpected content items count in raw request as Observable.", contentItems, - hasSize(writtenOnNextEvents.size())); - assertThat("Unexpected content items in raw request as Observable.", contentItems, - contains(writtenOnNextEvents.toArray())); - - return flushCount.get(); - } - - public void assertContentWrite(Observable content, Observable> newReq, - TestTrailerFactory tFactory, TestTrailerMutator tMutator, - int expectedFlushCounts) { - int flushCount = assertContentWrite(content, newReq, tFactory, tMutator); - assertThat("Unexpected flush counts", flushCount, is(expectedFlushCounts)); - } - - private RawRequest _assertContentWriteContentOnly(@SuppressWarnings("rawtypes") Observable contentWritten, - Observable> newReq) { - RawRequest rawRequest = getRawRequest(newReq); - - assertThat("Unexpected headers in the created raw request.", rawRequest.getHeaders(), - is(request.unsafeRawRequest().getHeaders())); - - assertThat("Unexpected content in the created raw request.", rawRequest.getContent(), is(contentWritten)); - return rawRequest; - } - - static RawRequest getRawRequest(Observable> newReq) { - assertThat("Unexpected request.", newReq, instanceOf(HttpClientRequestImpl.class)); - - HttpClientRequestImpl asClientReq = (HttpClientRequestImpl) newReq; - - return asClientReq.unsafeRawRequest(); - } - - public TestTrailerFactory newTrailerFactory() { - return new TestTrailerFactory(); - } - - public TestTrailerMutator newTrailerMutator() { - return new TestTrailerMutator<>(); - } - - @SuppressWarnings("unchecked") - public void addToConnectionInput(Object msg) { - if (null != cis) { - cis.onNext(msg); - } else { - throw new AssertionError("Connection input subscriber not found"); - } - } - } - - public static class TestTrailerFactory implements Func0 { - - private volatile TrailingHeaders lastReturned; - - @Override - public TrailingHeaders call() { - lastReturned = new TrailingHeaders(); - return lastReturned; - } - } - - public static class TestTrailerMutator implements Func2 { - - private volatile int callCount; - - @Override - public TrailingHeaders call(TrailingHeaders trailingHeaders,T content) { - callCount++; - return trailingHeaders; - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/internal/AbstractHttpConnectionBridgeTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/internal/AbstractHttpConnectionBridgeTest.java deleted file mode 100644 index 68daef6..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/internal/AbstractHttpConnectionBridgeTest.java +++ /dev/null @@ -1,522 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.internal; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.DefaultHttpContent; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultLastHttpContent; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.ConnectionInputSubscriberEvent; -import io.reactivex.netty.protocol.http.internal.AbstractHttpConnectionBridge.ConnectionInputSubscriber; -import io.reactivex.netty.protocol.http.internal.AbstractHttpConnectionBridgeTest.AbstractHttpConnectionBridgeMock.HttpObject; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.mockito.Matchers; -import org.mockito.Mockito; -import rx.Producer; -import rx.Subscriber; -import rx.functions.Action0; -import rx.observers.TestSubscriber; -import rx.subscriptions.Subscriptions; - -import java.nio.channels.ClosedChannelException; -import java.util.ArrayList; -import java.util.List; - -import static io.netty.handler.codec.http.HttpUtil.*; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class AbstractHttpConnectionBridgeTest { - - @Rule - public final HandlerRule handlerRule = new HandlerRule(); - - @Test(timeout = 60000) - public void testSetTransferEncoding() throws Exception { - DefaultHttpRequest reqWithNoContentLength = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"); - handlerRule.channel.writeAndFlush(reqWithNoContentLength); - - assertThat("Unexpected outbound message count.", handlerRule.channel.outboundMessages(), hasSize(1)); - - assertThat("Transfer encoding not set to chunked.", isTransferEncodingChunked(reqWithNoContentLength), - is(true)); - - DefaultHttpRequest reqWithContentLength = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"); - setContentLength(reqWithContentLength, 100); - - handlerRule.channel.writeAndFlush(reqWithContentLength); - - /*One header from previous write*/ - assertThat("Unexpected outbound message count.", handlerRule.channel.outboundMessages(), hasSize(2)); - - assertThat("Transfer encoding set to chunked when content length was set.", - isTransferEncodingChunked(reqWithContentLength), is(false)); - } - - @Test(timeout = 60000) - public void testWritePrimitives() throws Exception { - handlerRule.channel.writeAndFlush("Hello"); - - assertThat("Unexpected outbound message count.", handlerRule.channel.outboundMessages(), hasSize(1)); - assertThat("Unexpected message written.", handlerRule.channel.readOutbound(), instanceOf(ByteBuf.class)); - - handlerRule.channel.writeAndFlush("Hello".getBytes()); - - assertThat("Unexpected outbound message count.", handlerRule.channel.outboundMessages(), hasSize(1)); - assertThat("Unexpected message written.", handlerRule.channel.readOutbound(), instanceOf(ByteBuf.class)); - } - - @Test(timeout = 60000) - public void testConnInputSubscriberEvent() throws Exception { - handlerRule.setupAndAssertConnectionInputSub(); - } - - @Test(timeout = 60000) - public void testHttpContentSubscriberEventWithNoContentInputSub() throws Exception { - TestSubscriber subscriber = new TestSubscriber<>(); - handlerRule.channel.pipeline().fireUserEventTriggered(new HttpContentSubscriberEvent<>(subscriber)); - - subscriber.assertTerminalEvent(); - - assertThat("Subscriber did not get an error", subscriber.getOnErrorEvents(), hasSize(1)); - assertThat("Subscriber got an unexpected error", subscriber.getOnErrorEvents().get(0), - instanceOf(NullPointerException.class)); - } - - @Test(timeout = 60000) - public void testHttpContentSub() throws Exception { - handlerRule.setupAndAssertConnectionInputSub(); - handlerRule.simulateHeaderReceive(); /*Simulate header receive, required for content sub.*/ - ProducerAwareSubscriber subscriber = new ProducerAwareSubscriber<>(); - handlerRule.channel.pipeline().fireUserEventTriggered(new HttpContentSubscriberEvent<>(subscriber)); - - subscriber.assertNoErrors(); - - @SuppressWarnings("unchecked") - Subscriber contentSub = (Subscriber) handlerRule.connInSub.getState().getContentSub(); - - assertThat("Unexpected HTTP Content subscriber found", contentSub, equalTo((Subscriber)subscriber)); - assertThat("Unexpected content subscriber producer.", subscriber.getProducer(), - equalTo(handlerRule.connInputProducerMock)); - - subscriber.unsubscribe(); - - subscriber.assertUnsubscribed(); - - assertThat("Unsubscribing from HTTP content, did not unsubscribe from connection input.", - handlerRule.connInSub.isUnsubscribed(), is(true)); - } - - @Test(timeout = 60000) - public void testContentArrivedBeforeSubscription() throws Exception { - handlerRule.channel.config().setAutoRead(false); - - handlerRule.setupAndAssertConnectionInputSub(); - - handlerRule.connInSub.onNext(new DefaultLastHttpContent());/*Simulating content read on channel*/ - - TestSubscriber contentSub = new TestSubscriber<>(); - handlerRule.channel.pipeline().fireUserEventTriggered(new HttpContentSubscriberEvent<>(contentSub)); - - contentSub.assertTerminalEvent(); - - assertThat("Content received on delayed subscription.", contentSub.getOnNextEvents(), is(empty())); - assertThat("Error not received on delayed subscription.", contentSub.getOnErrorEvents(), hasSize(1)); - } - - @Test(timeout = 60000) - public void testLazyContentAndTrailerSubWithAutoReadOn() throws Exception { - handlerRule.channel.config().setAutoRead(true); - - handlerRule.setupAndAssertConnectionInputSub(); - - /*Request sent, no content/trailer sub registered, will cause error on sub.*/ - handlerRule.connInSub.onNext(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - - TestSubscriber contentSub = new TestSubscriber<>(); - /*Lazy subscription*/ - handlerRule.channel.pipeline().fireUserEventTriggered(new HttpContentSubscriberEvent<>(contentSub)); - - contentSub.assertTerminalEvent(); - - assertThat("Content received on lazy subscription.", contentSub.getOnNextEvents(), is(empty())); - assertThat("Error not received on lazy subscription.", contentSub.getOnErrorEvents(), hasSize(1)); - } - - @Test(timeout = 60000) - public void testLazyContentAndTrailerSubWithAutoReadOff() throws Exception { - handlerRule.channel.config().setAutoRead(false); - - handlerRule.setupAndAssertConnectionInputSub(); - - /*Request sent, after this it will expect the subscriber to be registered before content arrives..*/ - handlerRule.connInSub.onNext(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - - /*Content sent but no subscriber.*/ - handlerRule.connInSub.onNext(new DefaultHttpContent(Unpooled.buffer().writeBytes("Hello".getBytes()))); - - TestSubscriber contentSub = new TestSubscriber<>(); - /*Content already sent, lazy sub now.*/ - handlerRule.channel.pipeline().fireUserEventTriggered(new HttpContentSubscriberEvent<>(contentSub)); - - contentSub.assertTerminalEvent(); - - assertThat("Content received on lazy subscription.", contentSub.getOnNextEvents(), is(empty())); - assertThat("Error not received on lazy subscription.", contentSub.getOnErrorEvents(), hasSize(1)); - - handlerRule.connInSub.onNext(new DefaultLastHttpContent());/*Simulate completion.*/ - } - - @Test(timeout = 60000) - public void testHttpChunked() throws Exception { - handlerRule.setupAndAssertConnectionInputSub(); - handlerRule.simulateHeaderReceive(); - - /*Eager content subscription*/ - TestSubscriber contentSub = new TestSubscriber<>(); - handlerRule.channel.pipeline().fireUserEventTriggered(new HttpContentSubscriberEvent<>(contentSub)); - - /*Headers sent*/ - handlerRule.connInSub.onNext(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - - ByteBuf content1 = Unpooled.buffer().writeBytes("Hello".getBytes()); - ByteBuf content2 = Unpooled.buffer().writeBytes("Hello2".getBytes()); - ByteBuf contentLast = Unpooled.buffer().writeBytes("Hello3".getBytes()); - - /*Content 1 sent.*/ - handlerRule.connInSub.onNext(new DefaultHttpContent(content1)); - /*Content 2 sent.*/ - handlerRule.connInSub.onNext(new DefaultHttpContent(content2)); - - DefaultLastHttpContent trailers = new DefaultLastHttpContent(contentLast); - String trailer1Name = "foo"; - String trailer1Value = "bar"; - trailers.trailingHeaders().add(trailer1Name, trailer1Value); - - /*trailers with content*/ - handlerRule.connInSub.onNext(trailers); - - contentSub.assertTerminalEvent(); - contentSub.assertNoErrors(); - - assertThat("Unexpected content chunks.", contentSub.getOnNextEvents(), hasSize(3)); - assertThat("Unexpected content chunks.", contentSub.getOnNextEvents(), contains(content1, content2, - contentLast)); - } - - @Test(timeout = 60000) - public void testClose() throws Exception { - handlerRule.setupAndAssertConnectionInputSub(); - - /*Eager content subscription*/ - TestSubscriber contentSub = new TestSubscriber<>(); - handlerRule.channel.pipeline().fireUserEventTriggered(new HttpContentSubscriberEvent<>(contentSub)); - - /*Headers sent*/ - handlerRule.connInSub.onNext(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - - /*Close before response complete*/ - handlerRule.channel.close(); - - handlerRule.headerSub.assertTerminalEvent(); - contentSub.assertTerminalEvent(); - - assertThat("No error to header subscriber on close.", handlerRule.headerSub.getOnErrorEvents(), hasSize(1)); - assertThat("No error to content subscriber on close.", contentSub.getOnErrorEvents(), hasSize(1)); - - assertThat("Close before complete did not get invoked.", - ((AbstractHttpConnectionBridgeMock)handlerRule.handler).closedBeforeReceive, is(true)); - } - - @Test(timeout = 60000) - public void testHeaderUnsubscribeBeforeHeaderReceive() throws Exception { - handlerRule.setupAndAssertConnectionInputSub(); - - handlerRule.headerSub.unsubscribe(); - - assertThat("Connection input not unsubscribed.", handlerRule.connInSub.isUnsubscribed(), is(true)); - } - - @Test(timeout = 60000) - public void testHeaderUnsubscribeAfterHeaderReceive() throws Exception { - handlerRule.setupAndAssertConnectionInputSub(); - /*Headers sent*/ - handlerRule.connInSub.onNext(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - - handlerRule.headerSub.unsubscribe(); - - assertThat("Connection input unsubscribed post headers.", handlerRule.connInSub.isUnsubscribed(), is(false)); - } - - @Test(timeout = 60000) - public void testConnectionInputCompleteWithNoHeaders() throws Exception { - handlerRule.setupAndAssertConnectionInputSub(); - handlerRule.simulateHeaderReceive(); - - /*Eager content subscription*/ - TestSubscriber contentSub = new TestSubscriber<>(); - handlerRule.channel.pipeline().fireUserEventTriggered(new HttpContentSubscriberEvent<>(contentSub)); - - handlerRule.connInSub.onCompleted(); - - handlerRule.headerSub.assertTerminalEvent(); - /*Since headers started but not content*/ - handlerRule.headerSub.assertError(ClosedChannelException.class); - - contentSub.assertTerminalEvent(); - contentSub.assertError(ClosedChannelException.class); - } - - @Test(timeout = 60000) - public void testConnectionInputCompletePostHeaders() throws Exception { - handlerRule.setupAndAssertConnectionInputSub(); - - /*Eager content subscription*/ - TestSubscriber contentSub = new TestSubscriber<>(); - handlerRule.channel.pipeline().fireUserEventTriggered(new HttpContentSubscriberEvent<>(contentSub)); - - /*Headers sent*/ - handlerRule.connInSub.onNext(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - - handlerRule.headerSub.assertNoErrors(); - assertThat("Header subscriber did not get the headers.", handlerRule.headerSub.getOnNextEvents(), hasSize(1)); - /*Look only for one HTTP message*/ - handlerRule.headerSub.unsubscribe(); - assertThat("Content subscriber unsubscribed post header unsubscribe.", contentSub.isUnsubscribed(), is(false)); - - handlerRule.connInSub.onCompleted(); - - contentSub.assertTerminalEvent(); - assertThat("Content subscriber did not get an error.", contentSub.getOnErrorEvents(), hasSize(1)); - } - - @Test(timeout = 60000) - public void testMultiSubscribers() throws Exception { - handlerRule.setupAndAssertConnectionInputSub(); - handlerRule.simulateHeaderReceive(); - - /*Eager content subscription*/ - TestSubscriber contentSub = new TestSubscriber<>(); - handlerRule.channel.pipeline().fireUserEventTriggered(new HttpContentSubscriberEvent<>(contentSub)); - - @SuppressWarnings("unchecked") - Subscriber contentSubFound = (Subscriber) handlerRule.connInSub.getState().getContentSub(); - - assertThat("Unexpected HTTP Content subscriber found", contentSubFound, - equalTo((Subscriber) contentSub)); - - contentSub.assertNoErrors(); - - /*Second active subscription*/ - TestSubscriber contentSub2 = new TestSubscriber<>(); - handlerRule.channel.pipeline().fireUserEventTriggered(new HttpContentSubscriberEvent<>(contentSub2)); - - contentSub2.assertTerminalEvent(); - assertThat("Second content subscriber did not get an error.", contentSub2.getOnErrorEvents(), hasSize(1)); - } - - public static class HandlerRule extends ExternalResource { - - private Connection connMock; - private EmbeddedChannel channel; - private AbstractHttpConnectionBridge handler; - private EventCatcher eventCatcher; - private ConnectionInputSubscriber connInSub; - private Producer connInputProducerMock; - private ProducerAwareSubscriber headerSub; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - handler = newAbstractHttpConnectionBridgeMock(); - eventCatcher = new EventCatcher(); - channel = new EmbeddedChannel(handler, eventCatcher); - @SuppressWarnings("unchecked") - Connection connMock = Mockito.mock(Connection.class); - Mockito.when(connMock.unsafeNettyChannel()).thenReturn(channel); - - HandlerRule.this.connMock = connMock; - base.evaluate(); - } - }; - } - - protected AbstractHttpConnectionBridge newAbstractHttpConnectionBridgeMock() { - return new AbstractHttpConnectionBridgeMock(HttpRequest.class); - } - - public EmbeddedChannel getChannel() { - return channel; - } - - public void simulateHeaderReceive() { - connInSub.getState().headerReceived(); - } - - public void setupAndAssertConnectionInputSub() { - headerSub = new ProducerAwareSubscriber<>(); - - @SuppressWarnings({"rawtypes", "unchecked"}) - ConnectionInputSubscriberEvent evt = new ConnectionInputSubscriberEvent(headerSub); - - channel.pipeline().fireUserEventTriggered(evt); - - assertThat("Handler did not pass the event.", eventCatcher.events, hasSize(1)); - assertThat("Handler did not modify the event.", eventCatcher.events, not(contains((Object) evt))); - - Object eventCaught = eventCatcher.events.get(0); - - assertThat("Unexpected propagated event.", eventCaught, instanceOf(ConnectionInputSubscriberEvent.class)); - - @SuppressWarnings({"rawtypes", "unchecked"}) - ConnectionInputSubscriberEvent modEvt = (ConnectionInputSubscriberEvent) eventCaught; - - assertThat("Unexpected propagated event subscriber.", modEvt.getSubscriber(), - instanceOf(ConnectionInputSubscriber.class)); - - @SuppressWarnings("unchecked") - ConnectionInputSubscriber connInSub = (ConnectionInputSubscriber) modEvt.getSubscriber(); - this.connInSub = connInSub; - - assertThat("Channel not set in the subscriber.", connInSub.getChannel(), is(notNullValue())); - assertThat("Unexpected channel set in the subscriber.", connInSub.getChannel(), equalTo((Channel)channel)); - - @SuppressWarnings("unchecked") - Subscriber headerSub = (Subscriber) connInSub.getState().getHeaderSub(); - - assertThat("Unexpected header subscriber.", headerSub, is((Subscriber) this.headerSub)); - - connInputProducerMock = Mockito.mock(Producer.class); - connInSub.setProducer(connInputProducerMock); - - assertThat("Header subscriber producer not set.", this.headerSub.getProducer(), - equalTo(connInputProducerMock)); - - Mockito.verify(connInputProducerMock).request(Matchers.anyLong()); - } - - } - - private static class ProducerAwareSubscriber extends TestSubscriber { - - private Producer producer; - - @Override - public void setProducer(Producer producer) { - this.producer = producer; - super.setProducer(producer); - } - - public Producer getProducer() { - return producer; - } - } - - private static class EventCatcher extends ChannelDuplexHandler { - - private final List events = new ArrayList<>(); - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - events.add(evt); - super.userEventTriggered(ctx, evt); - } - } - - public static class AbstractHttpConnectionBridgeMock extends AbstractHttpConnectionBridge { - - private final Class headerMsgClass; - private volatile boolean closedBeforeReceive; - - public AbstractHttpConnectionBridgeMock(Class headerMsgClass) { - this.headerMsgClass = headerMsgClass; - } - - @Override - protected boolean isInboundHeader(Object nextItem) { - return headerMsgClass.isAssignableFrom(nextItem.getClass()); - } - - @Override - protected boolean isOutboundHeader(Object nextItem) { - return nextItem instanceof HttpRequest; - } - - @Override - protected Object newHttpObject(Object nextItem, Channel channel) { - return new HttpObject(); - } - - @Override - protected void onContentReceived() { - // No Op - } - - @Override - protected void onContentReceiveComplete(long receiveStartTimeNanos) { - // No Op - } - - @Override - protected void beforeOutboundHeaderWrite(HttpMessage httpMsg, ChannelPromise promise, long startTimeNanos) { - // No Op - } - - @Override - protected void onOutboundLastContentWrite(LastHttpContent msg, ChannelPromise promise, - long headerWriteStartTimeNanos) { - // No Op - } - - @Override - protected void onClosedBeforeReceiveComplete(Channel channel) { - closedBeforeReceive = true; - } - - @Override - protected void onNewContentSubscriber(final ConnectionInputSubscriber inputSubscriber, - Subscriber newSub) { - newSub.add(Subscriptions.create(new Action0() { - @Override - public void call() { - inputSubscriber.unsubscribe(); - } - })); - } - - public static class HttpObject { - } - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/CookieTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/CookieTest.java deleted file mode 100644 index b1dd532..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/CookieTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.cookie.Cookie; -import io.netty.handler.codec.http.cookie.DefaultCookie; -import io.netty.handler.codec.http.cookie.ServerCookieDecoder; -import io.netty.handler.logging.LoggingHandler; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.ConnectionImpl; -import io.reactivex.netty.events.EventAttributeKeys; -import io.reactivex.netty.test.util.MockEventPublisher; -import org.junit.Assert; -import org.junit.Test; - -import java.util.Map; -import java.util.Set; - -public class CookieTest { - - @Test(timeout = 60000) - public void testGetCookie() throws Exception { - DefaultHttpRequest nettyRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, ""); - String cookie1Name = "PREF"; - String cookie1Value = "ID=a95756377b78e75e:FF=0:TM=1392709628:LM=1392709628:S=a5mOVvTB7DBkexgi"; - String cookie1Header = cookie1Name + '=' + cookie1Value - + "; expires=Thu, 18-Feb-2016 07:47:08 GMT;"; - nettyRequest.headers().add(HttpHeaderNames.COOKIE, cookie1Header); - - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - HttpServerRequest request = new HttpServerRequestImpl<>(nettyRequest, channel); - - Map> cookies = request.getCookies(); - Assert.assertEquals("Unexpected number of cookies.", 1, cookies.size()); - Set cookies1 = cookies.get(cookie1Name); - Assert.assertNotNull("No cookie found with name: " + cookie1Name, cookies1); - Assert.assertEquals("Unexpected number of cookies with name: " + cookie1Name, 1, cookies1.size() ); - Cookie cookie = cookies1.iterator().next(); - Assert.assertEquals("Unexpected cookie name.", cookie1Name, cookie.name()); - } - - @Test(timeout = 60000) - public void testSetCookie() throws Exception { - DefaultHttpResponse nettyResponse = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND); - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - channel.attr(EventAttributeKeys.EVENT_PUBLISHER).set(MockEventPublisher.disabled()); - Connection connection = ConnectionImpl.fromChannel(channel); - HttpServerResponse response = HttpServerResponseImpl.create(null, connection, nettyResponse); - String cookieName = "name"; - String cookieValue = "value"; - response.addCookie(new DefaultCookie(cookieName, cookieValue)); - String cookieHeader = nettyResponse.headers().get(HttpHeaderNames.SET_COOKIE); - Assert.assertNotNull("Cookie header not found.", cookieHeader); - Set decode = ServerCookieDecoder.STRICT.decode(cookieHeader); - Assert.assertNotNull("Decoded cookie not found.", decode); - Assert.assertEquals("Unexpected number of decoded cookie not found.", 1, decode.size()); - Cookie cookie = decode.iterator().next(); - Assert.assertEquals("Unexpected cookie name.", cookieName, cookie.name()); - Assert.assertEquals("Unexpected cookie value.", cookieValue, cookie.value()); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/Http10Test.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/Http10Test.java deleted file mode 100644 index a8e070f..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/Http10Test.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpVersion; -import io.reactivex.netty.protocol.http.client.HttpClientRequest; -import io.reactivex.netty.protocol.http.client.HttpClientResponse; -import org.junit.Rule; -import org.junit.Test; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class Http10Test { - - @Rule - public final HttpServerRule rule = new HttpServerRule(); - - @Test(timeout = 60000) - public void testHttp1_0Response() throws Exception { - rule.setServer(rule.getServer().sendHttp10ResponseFor10Request(true)); - rule.startServer(); - - final HttpClientRequest request = - rule.getClient().createRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, "/"); - - final HttpClientResponse response = rule.sendRequest(request); - - assertThat("Unexpected HTTP version.", response.getHttpVersion(), is(HttpVersion.HTTP_1_0)); - assertThat("Unexpected keep-alive value.", response.isKeepAlive(), is(false)); - assertThat("Unexpected transfer encoding.", response.isTransferEncodingChunked(), is(false)); - - rule.assertResponseContent(response); - } - - @Test(timeout = 60000) - public void testHttp1_1Response() throws Exception { - rule.getServer().sendHttp10ResponseFor10Request(true); - rule.startServer(); - - final HttpClientRequest request = - rule.getClient().createRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, "/"); - - final HttpClientResponse response = rule.sendRequest(request); - - assertThat("Unexpected HTTP version.", response.getHttpVersion(), is(HttpVersion.HTTP_1_1)); - assertThat("Unexpected keep-alive value.", response.isKeepAlive(), is(false)); - assertThat("Unexpected transfer encoding.", response.isTransferEncodingChunked(), is(false)); - - rule.assertResponseContent(response); - } - -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpEndToEndTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpEndToEndTest.java deleted file mode 100644 index 110e832..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpEndToEndTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.reactivex.netty.protocol.http.server; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.reactivex.netty.protocol.http.client.HttpClientResponse; -import org.junit.Rule; -import org.junit.Test; -import rx.Observable; -import rx.Observable.OnSubscribe; -import rx.Scheduler.Worker; -import rx.Subscriber; -import rx.functions.Action0; -import rx.schedulers.Schedulers; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - -import static io.reactivex.netty.protocol.http.server.HttpServerRule.*; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class HttpEndToEndTest { - - @Rule - public final HttpServerRule rule = new HttpServerRule(); - - @Test(timeout = 60000) - public void testDelayedWrites() throws Exception { - - final AtomicReference errorFromWriteStreamCompletion = new AtomicReference<>(); - final Worker worker = Schedulers.computation().createWorker(); - rule.startServer(new RequestHandler() { - @Override - public Observable handle(HttpServerRequest request, HttpServerResponse response) { - return response.writeString(Observable.create(new OnSubscribe() { - @Override - public void call(final Subscriber subscriber) { - worker.schedule(new Action0() { - @Override - public void call() { - try { - subscriber.onNext(WELCOME_SERVER_MSG); - subscriber.onCompleted(); - } catch (Exception e) { - errorFromWriteStreamCompletion.set(e); - } - } - }, 1, TimeUnit.MILLISECONDS); - - } - })); - } - }); - - final HttpClientResponse response = rule.sendRequest(rule.getClient().createGet("/")); - - assertThat("Unexpected response code.", response.getStatus(), is(HttpResponseStatus.OK)); - - rule.assertResponseContent(response); - - assertThat("Unexpected exception on server.", errorFromWriteStreamCompletion.get(), is(nullValue())); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpServerRequestUriTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpServerRequestUriTest.java deleted file mode 100644 index b44b7b1..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpServerRequestUriTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.logging.LoggingHandler; -import org.junit.Assert; -import org.junit.Test; - -import java.util.List; -import java.util.Map; - -public class HttpServerRequestUriTest { - - @Test(timeout = 60000) - public void testRequestUri() throws Exception { - String path = "a/b/c"; - String qp1Name = "qp1"; - String qp1Val = "qp1Val"; - String qp2Name = "qp2"; - String qp2Val = "qp2Val"; - String qp2Val2 = "qp2Val222"; - String queryString = qp1Name + '=' + qp1Val + '&' + qp2Name + '=' + qp2Val + '&' + qp2Name + '=' + qp2Val2 ; - String uri = path + '?' + queryString; - DefaultHttpRequest nettyRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri); - HttpServerRequest request = newServerRequest(nettyRequest); - Assert.assertEquals("Unexpected uri string", uri, request.getUri()); - Assert.assertEquals("Unexpected query string", queryString, request.getRawQueryString()); - Assert.assertEquals("Unexpected path string", path, request.getDecodedPath()); - Map> qpsGot = request.getQueryParameters(); - Assert.assertNotNull("Got null query parameters", qpsGot); - Assert.assertEquals("Unexpected number of query parameters", 2, qpsGot.size()); - List qp1Got = qpsGot.get(qp1Name); - Assert.assertNotNull("Got no query parameters with name: " + qp1Name, qp1Got); - Assert.assertEquals("Unexpected number of query parameters with name: " + qp1Name, 1, qp1Got.size()); - Assert.assertEquals("Unexpected query parameter value with name: " + qp1Name, qp1Val, qp1Got.get(0)); - - List qp2Got = qpsGot.get(qp2Name); - Assert.assertNotNull("Got no query parameters with name: " + qp2Name, qp2Got); - Assert.assertEquals("Unexpected number of query parameters with name: " + qp2Name, 2, qp2Got.size()); - Assert.assertEquals("Unexpected query parameter value with name: " + qp2Name, qp2Val, qp2Got.get(0)); - Assert.assertEquals("Unexpected query parameter second value with name: " + qp2Name, qp2Val2, qp2Got.get(1)); - } - - @Test(timeout = 60000) - public void testEmptyQueryString() throws Exception { - String path = "a/b/c"; - String uri = path + '?'; - DefaultHttpRequest nettyRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri); - HttpServerRequest request = newServerRequest(nettyRequest); - Assert.assertEquals("Unexpected uri string", uri, request.getUri()); - Assert.assertEquals("Unexpected query string", "", request.getRawQueryString()); - } - - @Test(timeout = 60000) - public void testAbsentQueryString() throws Exception { - String uri = "a/b/c"; - DefaultHttpRequest nettyRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri); - HttpServerRequest request = newServerRequest(nettyRequest); - Assert.assertEquals("Unexpected uri string", uri, request.getUri()); - Assert.assertEquals("Unexpected query string", "", request.getRawQueryString()); - } - - protected HttpServerRequest newServerRequest(DefaultHttpRequest nettyRequest) { - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - return new HttpServerRequestImpl<>(nettyRequest, channel); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpServerRule.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpServerRule.java deleted file mode 100644 index b8dc794..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpServerRule.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.handler.logging.LogLevel; -import io.reactivex.netty.protocol.http.client.HttpClient; -import io.reactivex.netty.protocol.http.client.HttpClientResponse; -import org.junit.Assert; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.Observable; -import rx.functions.Action1; -import rx.functions.Func0; -import rx.functions.Func1; -import rx.observers.TestSubscriber; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.charset.Charset; -import java.util.regex.Pattern; - -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.hasSize; - -public class HttpServerRule extends ExternalResource { - - public static final String WELCOME_SERVER_MSG = "Welcome!"; - - private HttpServer server; - private HttpClient client; - - private String lastResponse = ""; - - @Override - public Statement apply(final Statement base, Description description) { - lastResponse = ""; - return new Statement() { - @Override - public void evaluate() throws Throwable { - server = HttpServer.newServer() - .enableWireLogging("test", LogLevel.INFO) - .addChannelHandlerFirst("raw-message-handler", - RawMessageHandler.factory( - new Action1() { - @Override - public void call(ByteBuf byteBuf) { - lastResponse += byteBuf.toString(Charset.defaultCharset()); - } - } - ) - ); - base.evaluate(); - } - }; - } - - public SocketAddress startServer() { - server.start(new RequestHandler() { - @Override - public Observable handle(HttpServerRequest request, - HttpServerResponse response) { - return response.setHeader(CONTENT_LENGTH, WELCOME_SERVER_MSG.getBytes().length) - .writeString(Observable.just(WELCOME_SERVER_MSG)); - } - }); - client = HttpClient.newClient("127.0.0.1", server.getServerPort()); - return server.getServerAddress(); - } - - public void startServer(RequestHandler handler) { - server.start(handler); - client = HttpClient.newClient("127.0.0.1", server.getServerPort()); - } - - public void setupClient(HttpClient client) { - this.client = client; - } - - public HttpClientResponse sendRequest(Observable> request) { - TestSubscriber> subscriber = new TestSubscriber<>(); - - request.subscribe(subscriber); - - subscriber.awaitTerminalEvent(); - subscriber.assertNoErrors(); - - assertThat("Unexpected response count.", subscriber.getOnNextEvents(), hasSize(1)); - return subscriber.getOnNextEvents().get(0); - } - - public void assertResponseContent(HttpClientResponse response) { - TestSubscriber subscriber = new TestSubscriber<>(); - - response.getContent().subscribe(subscriber); - - subscriber.awaitTerminalEvent(); - subscriber.assertNoErrors(); - - assertThat("Unexpected content items.", subscriber.getOnNextEvents(), hasSize(1)); - assertThat("Unexpected content.", subscriber.getOnNextEvents().get(0).toString(Charset.defaultCharset()), - equalTo(WELCOME_SERVER_MSG)); - } - - public void assertEmptyBodyWithContentLengthZero() { - assertBodyWithContentLength(0, ""); - } - - public void assertBodyWithContentLength(int contentLength, String body) { - getAndDrainClient(); - Pattern headerBlock = Pattern.compile("^(.*?\r\n)*?\r\n", Pattern.MULTILINE); - - if (!lastResponse.contains("content-length: " + contentLength + "\r\n")) { - Assert.fail("Missing header 'content-length: " + contentLength + "'"); - } - if (lastResponse.contains("transfer-encoding: chunked\r\n")) { - Assert.fail("Unexpected header 'transfer-encoding: chunked'"); - } - if (!headerBlock.matcher(lastResponse).replaceFirst("").equals(body)) { - Assert.fail("Unexpected body content '" + headerBlock.matcher(lastResponse).replaceFirst("") + "'"); - } - } - - public void assertEmptyBodyWithSingleChunk() { - assertChunks(); - } - - public void assertChunks(String... chunks) { - getAndDrainClient(); - Pattern headerBlock = Pattern.compile("^(.*?\r\n)*?\r\n", Pattern.MULTILINE); - - if (lastResponse.contains("content-length: 0\r\n")) { - Assert.fail("Unexpected header 'content-length: 0'"); - } - if (!lastResponse.contains("transfer-encoding: chunked\r\n")) { - Assert.fail("Missing header 'transfer-encoding: chunked'"); - } - String expectedChunkContent = ""; - for (String c : chunks) { - expectedChunkContent += c.getBytes().length + "\r\n"; - expectedChunkContent += c + "\r\n"; - } - expectedChunkContent += "0\r\n\r\n"; - if (!headerBlock.matcher(lastResponse).replaceFirst("").equals(expectedChunkContent)) { - Assert.fail("Unexpected body content '" + headerBlock.matcher(lastResponse).replaceFirst("") + "'"); - } - } - - public SocketAddress getServerAddress() { - return new InetSocketAddress("127.0.0.1", server.getServerPort()); - } - - public void setServer(HttpServer server) { - this.server = server; - } - - public HttpServer getServer() { - return server; - } - - public HttpClient getClient() { - return client; - } - - - public void getAndDrainClient() { - lastResponse = ""; - TestSubscriber clientDrain = new TestSubscriber<>(); - client.createGet("/") - .flatMap(new Func1, Observable>() { - @Override - public Observable call(HttpClientResponse clientResponse) { - return clientResponse.discardContent(); - } - }) - .subscribe(clientDrain); - clientDrain.awaitTerminalEvent(); - clientDrain.assertNoErrors(); - } - - - private static class RawMessageHandler extends ChannelDuplexHandler { - - public static Func0 factory(final Action1 onWrite) { - return new Func0() { - @Override - public ChannelHandler call() { - return new RawMessageHandler(onWrite); - } - }; - } - - private final Action1 onWrite; - - public RawMessageHandler(Action1 onWrite) { - this.onWrite = onWrite; - } - - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - callback(msg, onWrite); - super.write(ctx, msg, promise); - } - - private void callback(Object msg, Action1 a) { - if (msg instanceof ByteBuf) { - a.call((ByteBuf) msg); - } else if (msg instanceof ByteBufHolder) { - a.call(((ByteBufHolder) msg).content()); - } else { - throw new RuntimeException("Unexpected msg type " + msg.getClass()); - } - } - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpServerTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpServerTest.java deleted file mode 100644 index 4aa723f..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpServerTest.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpResponseStatus; -import org.junit.Rule; -import org.junit.Test; -import rx.Observable; - -public class HttpServerTest { - - @Rule - public final HttpServerRule serverRule = new HttpServerRule(); - - @Test(timeout = 60000) - public void testResponseWithNoContentLengthHeaderOrContentReturnsEmptyBody() throws Exception { - serverRule.startServer(new RequestHandler() { - @Override - public Observable handle(HttpServerRequest request, HttpServerResponse response) { - return response.setStatus(HttpResponseStatus.BAD_REQUEST); - } - }); - - serverRule.assertEmptyBodyWithContentLengthZero(); - } - - @Test(timeout = 60000) - public void testResponseWithNoContentLengthHeaderAndSendHeadersReturnsEmptyBody() throws Exception { - serverRule.startServer(new RequestHandler() { - @Override - public Observable handle(HttpServerRequest request, HttpServerResponse response) { - return response.setStatus(HttpResponseStatus.BAD_REQUEST) - .sendHeaders(); - } - }); - - serverRule.assertEmptyBodyWithContentLengthZero(); - } - - @Test(timeout = 60000) - public void testResponseWithNoContentLengthHeaderAndContentReturnsContentChunkAndSingleEmptyChunk() throws Exception { - serverRule.startServer(new RequestHandler() { - @Override - public Observable handle(HttpServerRequest request, HttpServerResponse response) { - return response.sendHeaders() - .writeString(Observable.just("Hello")); - } - }); - - serverRule.assertChunks("Hello"); - } - - @Test(timeout = 60000) - public void testResponseWithContentLengthReturnsRawBody() throws Exception { - serverRule.startServer(new RequestHandler() { - @Override - public Observable handle(HttpServerRequest request, HttpServerResponse response) { - return response.setStatus(HttpResponseStatus.BAD_REQUEST) - .setHeader(HttpHeaderNames.CONTENT_LENGTH, 5) - .writeString(Observable.just("Hello")); - } - }); - - serverRule.assertBodyWithContentLength(5, "Hello"); - } - - @Test(timeout = 60000) - public void testResponseWithZeroContentLengthReturnsEmptyBody() throws Exception { - serverRule.startServer(new RequestHandler() { - @Override - public Observable handle(HttpServerRequest request, HttpServerResponse response) { - return response.setStatus(HttpResponseStatus.BAD_REQUEST) - .setHeader(HttpHeaderNames.CONTENT_LENGTH, 0); - } - }); - - serverRule.assertEmptyBodyWithContentLengthZero(); - } - - @Test(timeout = 60000) - public void testResponseWithOnlyPositiveContentLengthReturnsEmptyBody() throws Exception { - serverRule.startServer(new RequestHandler() { - @Override - public Observable handle(HttpServerRequest request, HttpServerResponse response) { - return response.setStatus(HttpResponseStatus.BAD_REQUEST) - .setHeader(HttpHeaderNames.CONTENT_LENGTH, 5); - } - }); - - serverRule.assertEmptyBodyWithContentLengthZero(); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpServerToConnectionBridgeTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpServerToConnectionBridgeTest.java deleted file mode 100644 index 5e6dc00..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/HttpServerToConnectionBridgeTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.reactivex.netty.protocol.http.server; - -import io.reactivex.netty.protocol.http.internal.AbstractHttpConnectionBridge; -import io.reactivex.netty.protocol.http.internal.AbstractHttpConnectionBridgeTest.AbstractHttpConnectionBridgeMock; -import io.reactivex.netty.protocol.http.internal.AbstractHttpConnectionBridgeTest.HandlerRule; -import io.reactivex.netty.protocol.http.internal.HttpContentSubscriberEvent; -import io.reactivex.netty.protocol.http.server.events.HttpServerEventPublisher; -import io.reactivex.netty.protocol.tcp.server.events.TcpServerEventPublisher; -import org.junit.Rule; -import org.junit.Test; -import rx.observers.TestSubscriber; - -import java.nio.channels.ClosedChannelException; - -public class HttpServerToConnectionBridgeTest { - - @Rule - public final HandlerRule handlerRule = new HandlerRule() { - @Override - protected AbstractHttpConnectionBridge newAbstractHttpConnectionBridgeMock() { - return new HttpServerToConnectionBridge<>(new HttpServerEventPublisher(new TcpServerEventPublisher())); - } - }; - - @Test(timeout = 60000) - public void testPendingContentSubscriber() throws Exception { - handlerRule.setupAndAssertConnectionInputSub(); - handlerRule.simulateHeaderReceive(); /*Simulate header receive, required for content sub.*/ - TestSubscriber subscriber = new TestSubscriber<>(); - handlerRule.getChannel().pipeline().fireUserEventTriggered(new HttpContentSubscriberEvent<>(subscriber)); - TestSubscriber subscriber1 = new TestSubscriber<>(); - handlerRule.getChannel().pipeline().fireUserEventTriggered(new HttpContentSubscriberEvent<>(subscriber1)); - - subscriber.assertNoErrors(); - subscriber1.assertNoErrors(); - subscriber.unsubscribe(); - - subscriber.assertUnsubscribed(); - - handlerRule.getChannel().close().await(); - - subscriber.assertNoErrors(); - subscriber1.assertError(ClosedChannelException.class); - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/PipeliningTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/PipeliningTest.java deleted file mode 100644 index bd9a3b6..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/PipeliningTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server; - -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpClientCodec; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.protocol.tcp.client.TcpClient; -import org.junit.Rule; -import org.junit.Test; -import rx.Observable; -import rx.functions.Action1; -import rx.functions.Func1; -import rx.observers.TestSubscriber; - -import static io.netty.handler.codec.http.HttpMethod.*; -import static io.netty.handler.codec.http.HttpVersion.*; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class PipeliningTest { - - @Rule - public final HttpServerRule serverRule = new HttpServerRule(); - - @Test(timeout = 60000) - public void testPipelining() throws Exception { - - serverRule.startServer(); - - /*Since HTTP client does not yet support pipeling, this example uses a TCP client*/ - TestSubscriber testSubscriber = new TestSubscriber<>(); - TcpClient.newClient(serverRule.getServerAddress()) - .pipelineConfigurator(new Action1() { - @Override - public void call(ChannelPipeline pipeline) { - pipeline.addLast(new HttpClientCodec()); - pipeline.addLast(new HttpObjectAggregator(1024 * 1024)); - } - }) - .createConnectionRequest() - .flatMap(new Func1, Observable>() { - @Override - public Observable call(Connection c) { - DefaultFullHttpRequest request1 = new DefaultFullHttpRequest(HTTP_1_1, GET, "/1"); - DefaultFullHttpRequest request2 = new DefaultFullHttpRequest(HTTP_1_1, GET, "/2"); - return c.write(Observable.just(request1, request2)) - .ignoreElements() - .cast(FullHttpResponse.class) - .concatWith(c.getInput()); - } - } - ) - .map(new Func1() { - @Override - public String call(FullHttpResponse resp) { - return resp.status().toString(); - } - }) - .take(2) - .subscribe(testSubscriber); - - testSubscriber.awaitTerminalEvent(); - - testSubscriber.assertNoErrors(); - - assertThat("Unexpected number of responses.", testSubscriber.getOnNextEvents(), hasSize(2)); - assertThat("OK status code not found in response #1.", testSubscriber.getOnNextEvents().get(0), - containsString(HttpResponseStatus.OK.toString())); - assertThat("OK status code not found in response #2.", testSubscriber.getOnNextEvents().get(1), - containsString(HttpResponseStatus.OK.toString())); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/events/HttpServerEventPublisherTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/events/HttpServerEventPublisherTest.java deleted file mode 100644 index 4db23c9..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/events/HttpServerEventPublisherTest.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server.events; - -import io.reactivex.netty.protocol.http.server.events.HttpServerEventsListenerImpl.HttpEvent; -import io.reactivex.netty.protocol.tcp.server.events.TcpServerEventPublisher; -import io.reactivex.netty.test.util.MockConnectionEventListener.Event; -import io.reactivex.netty.test.util.MockTcpServerEventListener.ServerEvent; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class HttpServerEventPublisherTest { - - @Rule - public final PublisherRule rule = new PublisherRule(); - - @Test(timeout = 60000) - public void testOnNewRequestReceived() throws Exception { - rule.publisher.onNewRequestReceived(); - - rule.listener.assertMethodsCalled(HttpEvent.ReqRecv); - } - - @Test(timeout = 60000) - public void testOnRequestHandlingStart() throws Exception { - rule.publisher.onRequestHandlingStart(1, TimeUnit.MILLISECONDS); - - rule.listener.assertMethodsCalled(HttpEvent.HandlingStart); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testOnRequestHandlingSuccess() throws Exception { - rule.publisher.onRequestHandlingSuccess(1, TimeUnit.MILLISECONDS); - - rule.listener.assertMethodsCalled(HttpEvent.HandlingSuccess); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testOnRequestHandlingFailed() throws Exception { - final Throwable expected = new NullPointerException(); - - rule.publisher.onRequestHandlingFailed(1, TimeUnit.MILLISECONDS, expected); - - rule.listener.assertMethodsCalled(HttpEvent.HandlingFailed); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - assertThat("Listener not called with error.", rule.listener.getRecievedError(), is(expected)); - } - - @Test(timeout = 60000) - public void testOnRequestHeadersReceived() throws Exception { - rule.publisher.onRequestHeadersReceived(); - rule.listener.assertMethodsCalled(HttpEvent.ReqHdrsReceived); - } - - @Test(timeout = 60000) - public void testOnRequestContentReceived() throws Exception { - rule.publisher.onRequestContentReceived(); - rule.listener.assertMethodsCalled(HttpEvent.ReqContentReceived); - } - - @Test(timeout = 60000) - public void testOnRequestReceiveComplete() throws Exception { - rule.publisher.onRequestReceiveComplete(1, TimeUnit.MILLISECONDS); - rule.listener.assertMethodsCalled(HttpEvent.ReqReceiveComplete); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testOnResponseWriteStart() throws Exception { - rule.publisher.onResponseWriteStart(); - rule.listener.assertMethodsCalled(HttpEvent.RespWriteStart); - } - - @Test(timeout = 60000) - public void testOnResponseWriteSuccess() throws Exception { - rule.publisher.onResponseWriteSuccess(1, TimeUnit.MILLISECONDS, 200); - rule.listener.assertMethodsCalled(HttpEvent.RespWriteSuccess); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - assertThat("Listener not called with response code.", rule.listener.getResponseCode(), is(200)); - } - - @Test(timeout = 60000) - public void testOnResponseWriteFailed() throws Exception { - final Throwable expected = new NullPointerException(); - - rule.publisher.onResponseWriteFailed(1, TimeUnit.MILLISECONDS, expected); - - rule.listener.assertMethodsCalled(HttpEvent.RespWriteFailed); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - assertThat("Listener not called with error.", rule.listener.getRecievedError(), is(expected)); - } - - @Test(timeout = 60000) - public void testOnConnectionCloseFailed() throws Exception { - rule.publisher.onConnectionCloseFailed(1, TimeUnit.MILLISECONDS, new NullPointerException()); - - rule.listener.getTcpDelegate().getConnDelegate().assertMethodsCalled(Event.CloseFailed); - } - - @Test(timeout = 60000) - public void testOnConnectionCloseSuccess() throws Exception { - rule.publisher.onConnectionCloseSuccess(1, TimeUnit.MILLISECONDS); - - rule.listener.getTcpDelegate().getConnDelegate().assertMethodsCalled(Event.CloseSuccess); - } - - @Test(timeout = 60000) - public void testOnConnectionCloseStart() throws Exception { - rule.publisher.onConnectionCloseStart(); - - rule.listener.getTcpDelegate().getConnDelegate().assertMethodsCalled(Event.CloseStart); - } - - @Test(timeout = 60000) - public void testOnWriteFailed() throws Exception { - rule.publisher.onWriteFailed(1, TimeUnit.MILLISECONDS, new NullPointerException()); - - rule.listener.getTcpDelegate().getConnDelegate().assertMethodsCalled(Event.WriteFailed); - } - - @Test(timeout = 60000) - public void testOnWriteSuccess() throws Exception { - rule.publisher.onWriteSuccess(1, TimeUnit.MILLISECONDS); - - rule.listener.getTcpDelegate().getConnDelegate().assertMethodsCalled(Event.WriteSuccess); - } - - @Test(timeout = 60000) - public void testOnWriteStart() throws Exception { - rule.publisher.onWriteStart(); - - rule.listener.getTcpDelegate().getConnDelegate().assertMethodsCalled(Event.WriteStart); - } - - @Test(timeout = 60000) - public void testOnFlushSuccess() throws Exception { - rule.publisher.onFlushComplete(1, TimeUnit.MILLISECONDS); - - rule.listener.getTcpDelegate().getConnDelegate().assertMethodsCalled(Event.FlushSuccess); - } - - @Test(timeout = 60000) - public void testOnFlushStart() throws Exception { - rule.publisher.onFlushStart(); - - rule.listener.getTcpDelegate().getConnDelegate().assertMethodsCalled(Event.FlushStart); - } - - @Test(timeout = 60000) - public void testOnByteRead() throws Exception { - rule.publisher.onByteRead(1); - - rule.listener.getTcpDelegate().getConnDelegate().assertMethodsCalled(Event.BytesRead); - } - - @Test(timeout = 60000) - public void testOnByteWritten() throws Exception { - rule.publisher.onByteWritten(1); - - rule.listener.getTcpDelegate().getConnDelegate().assertMethodsCalled(Event.BytesWritten); - } - - @Test(timeout = 60000) - public void testOnConnectionHandlingFailed() throws Exception { - rule.publisher.onConnectionHandlingFailed(1, TimeUnit.MILLISECONDS, new NullPointerException()); - - rule.listener.getTcpDelegate().assertMethodsCalled(ServerEvent.HandlingFailed); - } - - @Test(timeout = 60000) - public void testOnConnectionHandlingSuccess() throws Exception { - rule.publisher.onConnectionHandlingSuccess(1, TimeUnit.MILLISECONDS); - - rule.listener.getTcpDelegate().assertMethodsCalled(ServerEvent.HandlingSuccess); - } - - @Test(timeout = 60000) - public void testOnConnectionHandlingStart() throws Exception { - rule.publisher.onConnectionHandlingStart(1, TimeUnit.MILLISECONDS); - - rule.listener.getTcpDelegate().assertMethodsCalled(ServerEvent.HandlingStart); - } - - @Test(timeout = 60000) - public void testOnNewClientConnected() throws Exception { - rule.publisher.onNewClientConnected(); - - rule.listener.getTcpDelegate().assertMethodsCalled(ServerEvent.NewClient); - } - - @Test(timeout = 60000) - public void testOnCustomEvent() throws Exception { - rule.publisher.onCustomEvent("Hello"); - rule.listener.getTcpDelegate().getConnDelegate().assertMethodsCalled(Event.CustomEvent); - } - - @Test(timeout = 60000) - public void testOnCustomEventWithError() throws Exception { - rule.publisher.onCustomEvent("Hello", new NullPointerException()); - rule.listener.getTcpDelegate().getConnDelegate().assertMethodsCalled(Event.CustomEventWithError); - } - - @Test(timeout = 60000) - public void testOnCustomEventWithDuration() throws Exception { - rule.publisher.onCustomEvent("Hello", 1, TimeUnit.MINUTES); - rule.listener.getTcpDelegate().getConnDelegate().assertMethodsCalled(Event.CustomEventWithDuration); - } - - @Test(timeout = 60000) - public void testOnCustomEventWithDurationAndError() throws Exception { - rule.publisher.onCustomEvent("Hello", 1, TimeUnit.MINUTES, new NullPointerException()); - rule.listener.getTcpDelegate().getConnDelegate().assertMethodsCalled(Event.CustomEventWithDurationAndError); - } - - @Test(timeout = 60000) - public void testPublishingEnabled() throws Exception { - assertThat("Publishing not enabled.", rule.publisher.publishingEnabled(), is(true)); - } - - @Test(timeout = 60000) - public void testCopy() throws Exception { - HttpServerEventPublisher copy = rule.publisher.copy(rule.publisher.getTcpDelegate().copy()); - - assertThat("Publisher not copied.", copy, is(not(sameInstance(rule.publisher)))); - assertThat("Listeners not copied.", copy.getListeners(), is(not(sameInstance(rule.publisher.getListeners())))); - assertThat("Delegate not copied.", copy.getTcpDelegate(), - is(not(sameInstance(rule.publisher.getTcpDelegate())))); - } - - public static class PublisherRule extends ExternalResource { - - private HttpServerEventsListenerImpl listener; - private HttpServerEventPublisher publisher; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - listener = new HttpServerEventsListenerImpl(); - publisher = new HttpServerEventPublisher(new TcpServerEventPublisher()); - publisher.subscribe(listener); - base.evaluate(); - } - }; - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/events/HttpServerEventsListenerImpl.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/events/HttpServerEventsListenerImpl.java deleted file mode 100644 index 191fb3f..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/server/events/HttpServerEventsListenerImpl.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.server.events; - -import io.reactivex.netty.test.util.MockTcpServerEventListener; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class HttpServerEventsListenerImpl extends HttpServerEventsListener { - - public enum HttpEvent { - ReqRecv, HandlingStart, HandlingSuccess, HandlingFailed, ReqHdrsReceived, ReqContentReceived, - ReqReceiveComplete, RespWriteStart, RespWriteSuccess, RespWriteFailed - } - - private final MockTcpServerEventListener tcpDelegate; - - private int responseCode; - private long duration; - private TimeUnit timeUnit; - private Throwable recievedError; - private final List methodsCalled = new ArrayList<>(); - - public HttpServerEventsListenerImpl() { - tcpDelegate = new MockTcpServerEventListener(); - } - - @Override - public void onNewRequestReceived() { - methodsCalled.add(HttpEvent.ReqRecv); - } - - @Override - public void onRequestHandlingStart(long duration, TimeUnit timeUnit) { - this.duration = duration; - this.timeUnit = timeUnit; - methodsCalled.add(HttpEvent.HandlingStart); - } - - @Override - public void onRequestHandlingSuccess(long duration, TimeUnit timeUnit) { - this.duration = duration; - this.timeUnit = timeUnit; - methodsCalled.add(HttpEvent.HandlingSuccess); - } - - @Override - public void onRequestHandlingFailed(long duration, TimeUnit timeUnit, Throwable recievedError) { - this.duration = duration; - this.timeUnit = timeUnit; - this.recievedError = recievedError; - methodsCalled.add(HttpEvent.HandlingFailed); - } - - @Override - public void onRequestHeadersReceived() { - methodsCalled.add(HttpEvent.ReqHdrsReceived); - } - - @Override - public void onRequestContentReceived() { - methodsCalled.add(HttpEvent.ReqContentReceived); - } - - @Override - public void onRequestReceiveComplete(long duration, TimeUnit timeUnit) { - this.duration = duration; - this.timeUnit = timeUnit; - methodsCalled.add(HttpEvent.ReqReceiveComplete); - } - - @Override - public void onResponseWriteStart() { - methodsCalled.add(HttpEvent.RespWriteStart); - } - - @Override - public void onResponseWriteSuccess(long duration, TimeUnit timeUnit, int responseCode) { - this.duration = duration; - this.timeUnit = timeUnit; - this.responseCode = responseCode; - methodsCalled.add(HttpEvent.RespWriteSuccess); - } - - @Override - public void onResponseWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - this.duration = duration; - this.timeUnit = timeUnit; - recievedError = throwable; - methodsCalled.add(HttpEvent.RespWriteFailed); - } - - @Override - public void onByteRead(long bytesRead) { - tcpDelegate.onByteRead(bytesRead); - } - - @Override - public void onByteWritten(long bytesWritten) { - tcpDelegate.onByteWritten(bytesWritten); - } - - @Override - public void onNewClientConnected() { - tcpDelegate.onNewClientConnected(); - } - - @Override - public void onConnectionHandlingStart(long duration, TimeUnit timeUnit) { - tcpDelegate.onConnectionHandlingStart(duration, timeUnit); - } - - @Override - public void onConnectionHandlingSuccess(long duration, TimeUnit timeUnit) { - tcpDelegate.onConnectionHandlingSuccess(duration, timeUnit); - } - - @Override - public void onConnectionHandlingFailed(long duration, TimeUnit timeUnit, - Throwable recievedError) { - tcpDelegate.onConnectionHandlingFailed(duration, timeUnit, recievedError); - } - - public void assertMethodsCalled(HttpEvent... events) { - assertThat("Unexpected methods called count.", methodsCalled, hasSize(events.length)); - assertThat("Unexpected methods called.", methodsCalled, contains(events)); - } - - public MockTcpServerEventListener getTcpDelegate() { - return tcpDelegate; - } - - public int getResponseCode() { - return responseCode; - } - - public long getDuration() { - return duration; - } - - public TimeUnit getTimeUnit() { - return timeUnit; - } - - public Throwable getRecievedError() { - return recievedError; - } - - @Override - public void onCompleted() { - tcpDelegate.onCompleted(); - } - - @Override - public void onConnectionCloseFailed(long duration, TimeUnit timeUnit, - Throwable recievedError) { - tcpDelegate.onConnectionCloseFailed(duration, timeUnit, recievedError); - } - - @Override - public void onConnectionCloseSuccess(long duration, TimeUnit timeUnit) { - tcpDelegate.onConnectionCloseSuccess(duration, timeUnit); - } - - @Override - public void onConnectionCloseStart() { - tcpDelegate.onConnectionCloseStart(); - } - - @Override - public void onWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - tcpDelegate.onWriteFailed(duration, timeUnit, throwable); - } - - @Override - public void onWriteSuccess(long duration, TimeUnit timeUnit) { - tcpDelegate.onWriteSuccess(duration, timeUnit); - } - - @Override - public void onWriteStart() { - tcpDelegate.onWriteStart(); - } - - @Override - public void onFlushComplete(long duration, TimeUnit timeUnit) { - tcpDelegate.onFlushComplete(duration, timeUnit); - } - - @Override - public void onFlushStart() { - tcpDelegate.onFlushStart(); - } - - @Override - public void onCustomEvent(Object event) { - tcpDelegate.onCustomEvent(event); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { - tcpDelegate.onCustomEvent(event, duration, timeUnit); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { - tcpDelegate.onCustomEvent(event, duration, timeUnit, throwable); - } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { - tcpDelegate.onCustomEvent(event, throwable); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/sse/ServerSentEventEndToEndTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/sse/ServerSentEventEndToEndTest.java deleted file mode 100644 index 727c8dd..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/sse/ServerSentEventEndToEndTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.sse; - -import io.netty.buffer.ByteBuf; -import io.reactivex.netty.protocol.http.client.HttpClient; -import io.reactivex.netty.protocol.http.client.HttpClientResponse; -import io.reactivex.netty.protocol.http.server.HttpServer; -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 org.junit.After; -import org.junit.Assert; -import org.junit.Test; -import rx.Observable; -import rx.functions.Func1; - -import java.util.concurrent.TimeUnit; - -import static rx.Observable.*; - -public class ServerSentEventEndToEndTest { - - private HttpServer sseServer; - - @After - public void tearDown() throws Exception { - if (null != sseServer) { - sseServer.shutdown(); - sseServer.awaitShutdown(1, TimeUnit.MINUTES); - } - } - - @Test(timeout = 60000) - public void testWriteRawString() throws Exception { - startServer(new Func1, Observable>() { - - @Override - public Observable call(HttpServerResponse response) { - return response.writeStringAndFlushOnEach(just("data: interval 1\n")); - } - }); - - receiveAndAssertSingleEvent(); - } - - @Test(timeout = 60000) - public void testWriteRawBytes() throws Exception { - startServer(new Func1, Observable>() { - - @Override - public Observable call(HttpServerResponse response) { - return response.writeBytesAndFlushOnEach(just("data: interval 1\n".getBytes())); - } - }); - - receiveAndAssertSingleEvent(); - } - - @Test(timeout = 60000) - public void testWriteServerSentEvent() throws Exception { - startServer(new Func1, Observable>() { - - @Override - public Observable call(HttpServerResponse response) { - return response.writeAndFlushOnEach(just(ServerSentEvent.withData("interval 1"))); - } - }); - - receiveAndAssertSingleEvent(); - } - - protected void receiveAndAssertSingleEvent() { - ServerSentEvent result = receivesSingleEvent(); - Assert.assertNotNull("Unexpected server sent event received.", result); - Assert.assertEquals("Unexpected event data.", "interval 1", result.contentAsString()); - Assert.assertNull("Unexpected event type.", result.getEventType()); - Assert.assertNull("Unexpected event id.", result.getEventId()); - result.release(); - } - - protected ServerSentEvent receivesSingleEvent() { - return receiveSse().take(1).map(new Func1() { - @Override - public ServerSentEvent call(ServerSentEvent serverSentEvent) { - serverSentEvent.retain(); - return serverSentEvent; - } - }).toBlocking().singleOrDefault(null); - } - - private Observable receiveSse() { - - return HttpClient.newClient("127.0.0.1", sseServer.getServerPort()) - .createGet("/") - .flatMap(new Func1, Observable>() { - @Override - public Observable call(HttpClientResponse resp) { - return resp.getContentAsServerSentEvents(); - } - }); - } - - private void startServer(final Func1, Observable> f) { - sseServer = HttpServer.newServer() - .start(new RequestHandler() { - @Override - public Observable handle(HttpServerRequest request, - HttpServerResponse response) { - return f.call(response.transformToServerSentEvents()); - } - }); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/sse/SseTestUtil.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/sse/SseTestUtil.java deleted file mode 100644 index fec51b4..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/sse/SseTestUtil.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.sse; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultHttpContent; -import io.netty.handler.codec.http.HttpContent; - -import java.nio.charset.Charset; - -import static org.junit.Assert.*; - -public final class SseTestUtil { - - private SseTestUtil() { - } - - public static ServerSentEvent newServerSentEvent(String eventType, String eventId, String data) { - ByteBuf eventTypeBuffer = null != eventType - ? Unpooled.buffer().writeBytes(eventType.getBytes(Charset.forName("UTF-8"))) - : null; - ByteBuf eventIdBuffer = null != eventId - ? Unpooled.buffer().writeBytes(eventId.getBytes(Charset.forName("UTF-8"))) - : null; - - ByteBuf dataBuffer = Unpooled.buffer().writeBytes(data.getBytes(Charset.forName("UTF-8"))); - - return ServerSentEvent.withEventIdAndType(eventIdBuffer, eventTypeBuffer, dataBuffer); - } - - public static String newSseProtocolString(String eventType, String eventId, String... dataElements) { - StringBuilder eventStream = new StringBuilder(); - - if (null != eventType) { - eventStream.append("event: ").append(eventType).append('\n'); - } - - if (null != eventId) { - eventStream.append("id: ").append(eventId).append('\n'); - } - - for (String aData : dataElements) { - eventStream.append("data: ").append(aData).append('\n'); - } - return eventStream.toString(); - } - - public static void assertContentEquals(String message, ByteBuf expected, ByteBuf actual) { - assertEquals(message, - null == expected ? null : expected.toString(Charset.defaultCharset()), - null == actual ? null : actual.toString(Charset.defaultCharset())); - } - - public static HttpContent toHttpContent(String event) { - ByteBuf in = Unpooled.buffer(1024); - in.writeBytes(event.getBytes(Charset.defaultCharset())); - return new DefaultHttpContent(in); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/sse/client/ServerSentEventDecoderTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/sse/client/ServerSentEventDecoderTest.java deleted file mode 100644 index 73e210e..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/sse/client/ServerSentEventDecoderTest.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.sse.client; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.logging.LoggingHandler; -import io.reactivex.netty.protocol.http.sse.ServerSentEvent; -import io.reactivex.netty.protocol.http.sse.ServerSentEvent.Type; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.List; - -import static io.reactivex.netty.protocol.http.sse.SseTestUtil.*; -import static org.junit.Assert.*; - -public class ServerSentEventDecoderTest { - - private final ServerSentEventDecoder decoder = new ServerSentEventDecoder(); - - private final ChannelHandlerContext ch = new EmbeddedChannel(new LoggingHandler()).pipeline().firstContext(); - - @Test(timeout = 60000) - public void testOneDataLineDecode() throws Exception { - String eventType = "add"; - String eventId = "1"; - String data = "data line"; - - ServerSentEvent expected = newServerSentEvent(eventType, eventId, data); - - doTest(newSseProtocolString(eventType, eventId, data), expected); - } - - @Test(timeout = 60000) - public void testMultipleDataLineDecode() throws Exception { - String eventType = "add"; - String eventId = "1"; - String data1 = "data line"; - String data2 = "data line"; - - ServerSentEvent expected1 = newServerSentEvent(eventType, eventId, data1); - ServerSentEvent expected2 = newServerSentEvent(eventType, eventId, data2); - - doTest(newSseProtocolString(eventType, eventId, data1, data2), expected1, expected2); - } - - @Test(timeout = 60000) - public void testEventWithNoIdDecode() throws Exception { - String eventType = "add"; - String data = "data line"; - - ServerSentEvent expected = newServerSentEvent(eventType, null, data); - - doTest(newSseProtocolString(eventType, null, data), expected); - } - - @Test(timeout = 60000) - public void testEventWithNoEventTypeDecode() throws Exception { - String eventId = "1"; - String data = "data line"; - - ServerSentEvent expected = newServerSentEvent(null, eventId, data); - - doTest(newSseProtocolString(null, eventId, data), expected); - } - - @Test(timeout = 60000) - public void testEventWithDataOnlyDecode() throws Exception { - String data = "data line"; - - ServerSentEvent expected = newServerSentEvent(null, null, data); - - doTest(newSseProtocolString(null, null, data), expected); - } - - @Test(timeout = 60000) - public void testResetEventType() throws Exception { - String eventType = "add"; - String eventId = "1"; - String data1 = "data line"; - String data2 = "data line"; - - ServerSentEvent expected1 = newServerSentEvent(eventType, eventId, data1); - ServerSentEvent expected2 = newServerSentEvent(null, eventId, data2); - - doTest(newSseProtocolString(eventType, eventId, data1) + newSseProtocolString("", null, data2), - expected1, expected2); - } - - @Test(timeout = 60000) - public void testResetEventId() throws Exception { - String eventType = "add"; - String eventId = "1"; - String data1 = "data line"; - String data2 = "data line"; - - ServerSentEvent expected1 = newServerSentEvent(eventType, eventId, data1); - ServerSentEvent expected2 = newServerSentEvent(eventType, null, data2); - - doTest(newSseProtocolString(eventType, eventId, data1) + newSseProtocolString(null, "", data2), - expected1, expected2); - } - - @Test(timeout = 60000) - public void testIncompleteEventId() throws Exception { - List out = new ArrayList<>(); - decoder.decode(ch, toHttpContent("id: 111"), out); - assertEquals("Unexpected number of decoded messages.", 0, out.size()); - - ServerSentEvent expected = newServerSentEvent(null, "1111", "data line"); - - doTest("1\ndata: data line\n", expected); - - } - - @Test(timeout = 60000) - public void testIncompleteEventType() throws Exception { - List out = new ArrayList<>(); - decoder.decode(ch, toHttpContent("event: ad"), out); - assertEquals("Unexpected number of decoded messages.", 0, out.size()); - - ServerSentEvent expected = newServerSentEvent("add", null, "data line"); - - doTest("d\ndata: data line\n", expected); - - } - - @Test(timeout = 60000) - public void testIncompleteEventData() throws Exception { - ServerSentEvent expected = newServerSentEvent("add", null, "data line"); - - List out = new ArrayList<>(); - - decoder.decode(ch, toHttpContent("event: add\n"), out); - assertEquals("Unexpected number of decoded messages.", 0, out.size()); - - decoder.decode(ch, toHttpContent("data: d"), out); - assertEquals("Unexpected number of decoded messages.", 0, out.size()); - - doTest("ata line\n", expected); - } - - @Test(timeout = 60000) - public void testIncompleteFieldName() throws Exception { - ServerSentEvent expected = newServerSentEvent("add", null, "data line"); - - List out = new ArrayList<>(); - - decoder.decode(ch, toHttpContent("ev"), out); - assertEquals("Unexpected number of decoded messages.", 0, out.size()); - - decoder.decode(ch, toHttpContent("ent: add\n d"), out); - assertEquals("Unexpected number of decoded messages.", 0, out.size()); - - doTest("ata: data line\n", expected); - } - - @Test(timeout = 60000) - public void testInvalidFieldNameAndNextEvent() throws Exception { - ArrayList out = new ArrayList<>(); - decoder.decode(ch, toHttpContent("event: event type\n"), out); - assertTrue("Output list not empty.", out.isEmpty()); - - decoder.decode(ch, toHttpContent("data: dumb \n"), out); - assertFalse("Event not emitted after invalid field name.", out.isEmpty()); - assertEquals("Unexpected event count after invalid field name.", 1, out.size()); - - } - - @Test(timeout = 60000) - public void testInvalidFieldName() throws Throwable { - ArrayList out = new ArrayList<>(); - decoder.decode(ch, toHttpContent("event: dumb \n"), out); - assertTrue("Event emitted for invalid field name.", out.isEmpty()); - } - - @Test(timeout = 60000) - public void testFieldNameWithSpace() throws Throwable { - ArrayList out = new ArrayList<>(); - decoder.decode(ch, toHttpContent("eve nt: dumb \n"), new ArrayList<>()); - assertTrue("Event emitted for invalid field name.", out.isEmpty()); - } - - @Test(timeout = 60000) - public void testDataInMultipleChunks() throws Exception { - ServerSentEvent expected = newServerSentEvent(null, null, "data line"); - - List out = new ArrayList<>(); - - decoder.decode(ch, toHttpContent("da"), out); - assertEquals("Unexpected number of decoded messages.", 0, out.size()); - - decoder.decode(ch, toHttpContent("ta: d"), out); - assertEquals("Unexpected number of decoded messages.", 0, out.size()); - - decoder.decode(ch, toHttpContent("ata"), out); - assertEquals("Unexpected number of decoded messages.", 0, out.size()); - - decoder.decode(ch, toHttpContent(" "), out); - assertEquals("Unexpected number of decoded messages.", 0, out.size()); - - decoder.decode(ch, toHttpContent("li"), out); - assertEquals("Unexpected number of decoded messages.", 0, out.size()); - - decoder.decode(ch, toHttpContent("ne"), out); - assertEquals("Unexpected number of decoded messages.", 0, out.size()); - - doTest("\n", expected); - } - - @Test(timeout = 10000) - public void testLeadingNewLineInFieldName() throws Exception { - List out = new ArrayList<>(); - decoder.decode(ch, toHttpContent("\n data: ad\n"), out); - assertEquals("Unexpected number of decoded messages.", 1, out.size()); - } - - @Test(timeout = 10000) - public void testLeadingSpaceInFieldName() throws Exception { - List out = new ArrayList<>(); - decoder.decode(ch, toHttpContent(" data: ad\n"), out); - assertEquals("Unexpected number of decoded messages.", 1, out.size()); - - ServerSentEvent event = (ServerSentEvent) out.get(0); - assertEquals("Unexpected event type.", Type.Data, event.getType()); - assertEquals("Unexpected event type.", "ad", event.contentAsString()); - } - - private void doTest(String eventText, ServerSentEvent... expected) throws Exception { - List out = new ArrayList<>(); - decoder.decode(ch, toHttpContent(eventText), out); - - assertEquals(expected.length, out.size()); - - for (int i = 0; i < out.size(); i++) { - ServerSentEvent event = (ServerSentEvent) out.get(i); - assertContentEquals("Unexpected SSE data", expected[i].content(), event.content()); - assertContentEquals("Unexpected SSE event type", expected[i].getEventType(), - event.getEventType()); - assertContentEquals("Unexpected SSE event id", expected[i].getEventId(), event.getEventId()); - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/sse/server/ServerSentEventEncoderTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/sse/server/ServerSentEventEncoderTest.java deleted file mode 100644 index cadc256..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/sse/server/ServerSentEventEncoderTest.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.sse.server; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.reactivex.netty.protocol.http.sse.ServerSentEvent; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import java.nio.charset.Charset; - -import static io.reactivex.netty.protocol.http.sse.SseTestUtil.*; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class ServerSentEventEncoderTest { - - @Rule - public final EncoderRule rule = new EncoderRule(); - - @Test(timeout = 60000) - public void testOneDataLineEncode() throws Exception { - String eventType = "add"; - String eventId = "1"; - String data = "data line"; - ServerSentEvent event = newServerSentEvent(eventType, eventId, data); - String expectedOutput = newSseProtocolString(eventType, eventId, data); - rule.test(expectedOutput, event); - } - - @Test(timeout = 60000) - public void testMultipleDataLineEncode() throws Exception { - ServerSentEventEncoder splitEncoder = new ServerSentEventEncoder(true); - EmbeddedChannel channel = new EmbeddedChannel(splitEncoder); - - String eventType = "add"; - String eventId = "1"; - String data1 = "first line"; - String data2 = "second line"; - String data3 = "third line"; - String data = data1 + '\n' + data2 + '\n' + data3; - ServerSentEvent event = newServerSentEvent(eventType, eventId, data); - String expectedOutput = newSseProtocolString(eventType, eventId, data1, data2, data3); - rule.test(channel, expectedOutput, event); - } - - @Test(timeout = 60000) - public void testNoSplitMode() throws Exception { - String eventType = "add"; - String eventId = "1"; - String data = "first line\nsecond line\nthird line"; - ServerSentEvent event = newServerSentEvent(eventType, eventId, data); - String expectedOutput = newSseProtocolString(eventType, eventId, data); - rule.test(expectedOutput, event); - } - - @Test(timeout = 60000) - public void testEventWithNoIdEncode() throws Exception { - String eventType = "add"; - String data = "data line"; - ServerSentEvent event = newServerSentEvent(eventType, null, data); - String expectedOutput = newSseProtocolString(eventType, null, data); - rule.test(expectedOutput, event); - } - - @Test(timeout = 60000) - public void testEventWithNoEventTypeEncode() throws Exception { - String eventId = "1"; - String data = "data line"; - ServerSentEvent event = newServerSentEvent(null, eventId, data); - String expectedOutput = newSseProtocolString(null, eventId, data); - rule.test(expectedOutput, event); - } - - @Test(timeout = 60000) - public void testEventWithDataOnlyEncode() throws Exception { - String data = "data line"; - ServerSentEvent event = newServerSentEvent(null, null, data); - String expectedOutput = newSseProtocolString(null, null, data); - rule.test(expectedOutput, event); - } - - public static class EncoderRule extends ExternalResource { - - private ServerSentEventEncoder encoder; - private EmbeddedChannel channel; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - encoder = new ServerSentEventEncoder(); - channel = new EmbeddedChannel(encoder); - base.evaluate(); - } - }; - } - - public void test(String expectedOutput, ServerSentEvent... toEncode) { - test(channel, expectedOutput, toEncode); - } - - public void test(EmbeddedChannel channel, String expectedOutput, ServerSentEvent... toEncode) { - - for (ServerSentEvent event : toEncode) { - channel.writeAndFlush(event); - } - - final ByteBuf allOut = Unpooled.buffer(); - ByteBuf anOut; - while ((anOut = channel.readOutbound()) != null) { - allOut.writeBytes(anOut); - } - - assertThat("Unexpected encoder output", allOut.toString(Charset.defaultCharset()), equalTo(expectedOutput)); - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/ws/client/OperatorCacheSingleWebsocketConnectionTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/ws/client/OperatorCacheSingleWebsocketConnectionTest.java deleted file mode 100644 index 87b5277..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/ws/client/OperatorCacheSingleWebsocketConnectionTest.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.ws.client; - -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.protocol.http.ws.WebSocketConnection; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import rx.Observable; -import rx.Observable.OnSubscribe; -import rx.Subscriber; -import rx.functions.Func1; -import rx.observers.TestSubscriber; -import rx.subjects.Subject; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ConcurrentLinkedQueue; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class OperatorCacheSingleWebsocketConnectionTest { - - @Rule - public final OpRule opRule = new OpRule(); - - @Test(timeout = 60000) - public void testLifecycleNeverEnds() throws Exception { - opRule.subscribeAndAssertValues(opRule.getSourceWithCache().repeat(2), 1); - } - - @Test(timeout = 60000) - public void testLifecycleCompletesImmediately() throws Exception { - opRule.subscribeAndAssertValues(opRule.getSourceWithCache() - .map(new Func1() { - @Override - public WebSocketConnection call(WebSocketConnection c) { - opRule.terminateFirstConnection(null); - return c; - } - }).repeat(2), - 2);// Since the cached item is immediately invalid, two items will be emitted from source. - } - - @Test(timeout = 60000) - public void testLifecycleErrorsImmediately() throws Exception { - opRule.subscribeAndAssertValues(opRule.getSourceWithCache() - .map(new Func1() { - @Override - public WebSocketConnection call(WebSocketConnection c) { - opRule.terminateFirstConnection(new IllegalStateException()); - return c; - } - }).repeat(2), - 2);// Since the cached item is immediately invalid, two items will be emitted from source. - } - - @Test(timeout = 60000) - public void testSourceEmitsNoItems() throws Exception { - TestSubscriber subscriber = new TestSubscriber<>(); - - Observable.>>empty() - .lift(new OperatorCacheSingleWebsocketConnection()) - .subscribe(subscriber); - - subscriber.awaitTerminalEvent(); - subscriber.assertError(IllegalStateException.class); - } - - @Test(timeout = 60000) - public void testSourceEmitsError() throws Exception { - TestSubscriber subscriber = new TestSubscriber<>(); - - Observable.>error(new NullPointerException()) - .nest() - .lift(new OperatorCacheSingleWebsocketConnection()) - .subscribe(subscriber); - - subscriber.awaitTerminalEvent(); - subscriber.assertError(NullPointerException.class); - } - - @Test(timeout = 60000) - public void testUnsubscribeFromLifecycle() throws Exception { - - opRule.subscribeAndAssertValues(opRule.getSourceWithCache(), 1); - LifecycleSubject lifecycleSubject = opRule.lifecycles.poll(); - assertThat("No subscribers to lifecycle.", lifecycleSubject.subscribers, hasSize(1)); - assertThat("Lifecycle subscriber not unsubscribed.", lifecycleSubject.subscribers.poll().isUnsubscribed(), - is(true)); - } - - private static class OpRule extends ExternalResource { - - private Observable>> source; - private ConcurrentLinkedQueue lifecycles; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - lifecycles = new ConcurrentLinkedQueue<>(); - source = Observable.create(new OnSubscribe>() { - @Override - public void call(Subscriber> s) { - LifecycleSubject l = new LifecycleSubject(new ConcurrentLinkedQueue>()); - lifecycles.add(l); - WebSocketConnection conn = newConnection(l); - /*Subscriptions to the emitted Observable will always give the same connection but - subscription to the source (this Observable) creates a new connection. This simulates - how the actual websocket connection get works.*/ - s.onNext(Observable.just(conn)); - s.onCompleted(); - - } - }).nest(); - base.evaluate(); - } - }; - } - - public boolean terminateFirstConnection(Throwable error) { - LifecycleSubject poll = lifecycles.poll(); - - if (null != poll) { - if (null == error) { - poll.onCompleted(); - } else { - poll.onError(error); - } - return true; - } - - return false; - } - - public Observable getSourceWithCache() { - return source.lift(new OperatorCacheSingleWebsocketConnection()); - } - - public void subscribeAndAssertValues(Observable source, int distinctItemsCount) { - TestSubscriber subscriber = new TestSubscriber<>(); - - source.subscribe(subscriber); - - subscriber.awaitTerminalEvent(); - subscriber.assertNoErrors(); - - List onNextEvents = subscriber.getOnNextEvents(); - Set distinctConns = new HashSet<>(onNextEvents); - assertThat("Unexpected number of distinct connections.", distinctConns, hasSize(distinctItemsCount)); - } - - private WebSocketConnection newConnection(final Observable lifecycle) { - @SuppressWarnings("unchecked") - Connection mock = Mockito.mock(Connection.class); - Mockito.when(mock.closeListener()).thenAnswer(new Answer>() { - @Override - public Observable answer(InvocationOnMock invocation) throws Throwable { - return lifecycle; - } - }); - return new WebSocketConnection(mock); - } - } - - private static class LifecycleSubject extends Subject { - - private final ConcurrentLinkedQueue> subscribers; - - protected LifecycleSubject(final ConcurrentLinkedQueue> subscribers) { - super(new OnSubscribe() { - @Override - public void call(Subscriber subscriber) { - subscribers.add(subscriber); - } - }); - this.subscribers = subscribers; - } - - @Override - public boolean hasObservers() { - return !subscribers.isEmpty(); - } - - @Override - public void onCompleted() { - for (Subscriber subscriber : subscribers) { - subscriber.onCompleted(); - } - } - - @Override - public void onError(Throwable e) { - for (Subscriber subscriber : subscribers) { - subscriber.onError(e); - } - } - - @Override - public void onNext(Void aVoid) { - // No op ... - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/ws/server/WSEagerInputSubscriptionHandlerTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/ws/server/WSEagerInputSubscriptionHandlerTest.java deleted file mode 100644 index bdcd8d6..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/http/ws/server/WSEagerInputSubscriptionHandlerTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.http.ws.server; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.reactivex.netty.protocol.http.client.HttpClient; -import io.reactivex.netty.protocol.http.server.HttpServer; -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.protocol.http.ws.WebSocketConnection; -import io.reactivex.netty.protocol.http.ws.client.WebSocketResponse; -import org.junit.Test; -import rx.Observable; -import rx.functions.Func1; -import rx.observers.TestSubscriber; - -public class WSEagerInputSubscriptionHandlerTest { - - @Test(timeout = 60000) - public void testHandlerSubscribesEagerly() throws Exception { - HttpServer server = - HttpServer.newServer() - .start(new RequestHandler() { - @Override - public Observable handle(HttpServerRequest request, - HttpServerResponse response) { - if (request.isWebSocketUpgradeRequested()) { - return response.acceptWebSocketUpgrade( - new WebSocketHandler() { - @Override - public Observable handle(WebSocketConnection wsConnection) { - wsConnection.getInput().subscribe(); - return Observable.never(); - } - }); - } else { - return response.setStatus(HttpResponseStatus.NOT_FOUND); - } - } - }); - - TestSubscriber subscriber = new TestSubscriber<>(); - HttpClient.newClient(server.getServerAddress()) - .createGet("/ws") - .requestWebSocketUpgrade() - .flatMap(new Func1, Observable>() { - @Override - public Observable call(WebSocketResponse wsResp) { - if (wsResp.isUpgraded()) { - return Observable.empty(); - } - return Observable.error(new IllegalStateException("WebSocket upgrade not accepted.")); - } - }) - .subscribe(subscriber); - - subscriber.awaitTerminalEvent(); - subscriber.assertNoErrors(); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/EventListenerTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/EventListenerTest.java deleted file mode 100644 index ae1a485..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/EventListenerTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.logging.LogLevel; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.protocol.tcp.server.ConnectionHandler; -import io.reactivex.netty.protocol.tcp.server.TcpServer; -import io.reactivex.netty.test.util.MockConnectionEventListener.Event; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.Observable; -import rx.functions.Func1; -import rx.observers.TestSubscriber; - -import java.net.SocketAddress; - -public class EventListenerTest { - - @Rule - public final TcpServerRule rule = new TcpServerRule(); - - @Test(timeout = 60000) - public void testEventListener() throws Exception { - TcpClient client = TcpClient.newClient(rule.serverAddress); - - assertListenerCalled(client); - } - - @Test(timeout = 60000) - public void testEventListenerPostCopy() throws Exception { - TcpClient client = TcpClient.newClient(rule.serverAddress) - .enableWireLogging("test", LogLevel.ERROR); - - assertListenerCalled(client); - } - - @Test(timeout = 60000) - public void testSubscriptionPreCopy() throws Exception { - TcpClient client = TcpClient.newClient(rule.serverAddress); - - MockTcpClientEventListener listener = subscribe(client); - - client = client.enableWireLogging("test", LogLevel.DEBUG); - - connectAndAssertListenerInvocation(client, listener); - } - - private static void assertListenerCalled(TcpClient client) { - MockTcpClientEventListener listener = subscribe(client); - connectAndAssertListenerInvocation(client, listener); - } - - private static void connectAndAssertListenerInvocation(TcpClient client, - MockTcpClientEventListener listener) { - TestSubscriber subscriber = new TestSubscriber<>(); - client.createConnectionRequest().flatMap(new Func1, Observable>() { - @Override - public Observable call(Connection c) { - return c.getInput(); - } - }).take(1).subscribe(subscriber); - - subscriber.awaitTerminalEvent(); - subscriber.assertNoErrors(); - - listener.assertMethodsCalled(Event.BytesRead); - } - - private static MockTcpClientEventListener subscribe(TcpClient client) { - MockTcpClientEventListener listener = new MockTcpClientEventListener(); - client.subscribe(listener); - return listener; - } - - public static class TcpServerRule extends ExternalResource { - - private SocketAddress serverAddress; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - serverAddress = TcpServer.newServer().start(new ConnectionHandler() { - @Override - public Observable handle(Connection newConnection) { - return newConnection.writeString(Observable.just("Hello")); - } - }).getServerAddress(); - base.evaluate(); - } - }; - } - } - -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/MockTcpClientEventListener.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/MockTcpClientEventListener.java deleted file mode 100644 index f499278..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/MockTcpClientEventListener.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client; - -import io.reactivex.netty.protocol.tcp.client.events.TcpClientEventListener; -import io.reactivex.netty.test.util.MockClientEventListener; -import io.reactivex.netty.test.util.MockClientEventListener.ClientEvent; -import io.reactivex.netty.test.util.MockConnectionEventListener.Event; - -import java.util.concurrent.TimeUnit; - -public class MockTcpClientEventListener extends TcpClientEventListener { - - private final MockClientEventListener mockDelegate = new MockClientEventListener(); - - public void assertMethodCalled(ClientEvent event) { - mockDelegate.assertMethodCalled(event); - } - - public void assertMethodsCalled(ClientEvent... events) { - mockDelegate.assertMethodsCalled(events); - } - - public void assertMethodCalled(Event event) { - mockDelegate.assertMethodCalled(event); - } - - public void assertMethodsCalled(Event... events) { - mockDelegate.assertMethodsCalled(events); - } - - public long getDuration() { - return mockDelegate.getDuration(); - } - - public Throwable getRecievedError() { - return mockDelegate.getRecievedError(); - } - - public TimeUnit getTimeUnit() { - return mockDelegate.getTimeUnit(); - } - - @Override - public void onByteRead(long bytesRead) { - mockDelegate.onByteRead(bytesRead); - } - - @Override - public void onByteWritten(long bytesWritten) { - mockDelegate.onByteWritten(bytesWritten); - } - - @Override - public void onCompleted() { - mockDelegate.onCompleted(); - } - - @Override - public void onConnectFailed(long duration, TimeUnit timeUnit, Throwable recievedError) { - mockDelegate.onConnectFailed(duration, timeUnit, recievedError); - } - - @Override - public void onConnectionCloseFailed(long duration, TimeUnit timeUnit, - Throwable recievedError) { - mockDelegate.onConnectionCloseFailed(duration, timeUnit, recievedError); - } - - @Override - public void onConnectionCloseStart() { - mockDelegate.onConnectionCloseStart(); - } - - @Override - public void onConnectionCloseSuccess(long duration, TimeUnit timeUnit) { - mockDelegate.onConnectionCloseSuccess(duration, timeUnit); - } - - @Override - public void onConnectStart() { - mockDelegate.onConnectStart(); - } - - @Override - public void onConnectSuccess(long duration, TimeUnit timeUnit) { - mockDelegate.onConnectSuccess(duration, timeUnit); - } - - @Override - public void onCustomEvent(Object event) { - mockDelegate.onCustomEvent(event); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { - mockDelegate.onCustomEvent(event, duration, timeUnit); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, - Throwable throwable) { - mockDelegate.onCustomEvent(event, duration, timeUnit, throwable); - } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { - mockDelegate.onCustomEvent(event, throwable); - } - - @Override - public void onFlushStart() { - mockDelegate.onFlushStart(); - } - - @Override - public void onFlushComplete(long duration, TimeUnit timeUnit) { - mockDelegate.onFlushComplete(duration, timeUnit); - } - - @Override - public void onPoolAcquireFailed(long duration, TimeUnit timeUnit, - Throwable recievedError) { - mockDelegate.onPoolAcquireFailed(duration, timeUnit, recievedError); - } - - @Override - public void onPoolAcquireStart() { - mockDelegate.onPoolAcquireStart(); - } - - @Override - public void onPoolAcquireSuccess(long duration, TimeUnit timeUnit) { - mockDelegate.onPoolAcquireSuccess(duration, timeUnit); - } - - @Override - public void onPooledConnectionEviction() { - mockDelegate.onPooledConnectionEviction(); - } - - @Override - public void onPooledConnectionReuse() { - mockDelegate.onPooledConnectionReuse(); - } - - @Override - public void onPoolReleaseFailed(long duration, TimeUnit timeUnit, - Throwable recievedError) { - mockDelegate.onPoolReleaseFailed(duration, timeUnit, recievedError); - } - - @Override - public void onPoolReleaseStart() { - mockDelegate.onPoolReleaseStart(); - } - - @Override - public void onPoolReleaseSuccess(long duration, TimeUnit timeUnit) { - mockDelegate.onPoolReleaseSuccess(duration, timeUnit); - } - - @Override - public void onWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - mockDelegate.onWriteFailed(duration, timeUnit, throwable); - } - - @Override - public void onWriteStart() { - mockDelegate.onWriteStart(); - } - - @Override - public void onWriteSuccess(long duration, TimeUnit timeUnit) { - mockDelegate.onWriteSuccess(duration, timeUnit); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/PoolingWithRealChannelTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/PoolingWithRealChannelTest.java deleted file mode 100644 index 12ab0eb..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/PoolingWithRealChannelTest.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedChannel; -import io.reactivex.netty.client.pool.PooledConnection; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import rx.Observable; -import rx.functions.Func0; -import rx.functions.Func1; -import rx.observers.AssertableSubscriber; - -import java.util.ArrayList; -import java.util.List; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.is; -import static rx.Observable.fromCallable; -import static rx.Observable.just; - -/** - * This tests the code paths which are not invoked for {@link EmbeddedChannel} as it does not schedule any task - * (an EmbeddedChannelEventLopp never returns false for isInEventLoop()) - */ -@Ignore -public class PoolingWithRealChannelTest { - - @Rule - public final TcpClientRule clientRule = new TcpClientRule(); - - /** - * This test validates the async onNext and synchronous onComplete/onError nature of pooling when the connection is - * reused. - */ - //@Test(timeout = 60000) - public void testReuse() throws Exception { - clientRule.startServer(1); - PooledConnection connection = clientRule.connect(); - connection.closeNow(); - assertThat("Pooled connection is closed.", connection.unsafeNettyChannel().isOpen(), is(true)); - PooledConnection connection2 = clientRule.connect(); - assertThat("Connection is not reused.", connection2, is(connection)); - } - - /** - * Load test to prove concurrency issues mainly seen on heavy load. - */ - @Test - public void testLoad() { - clientRule.startServer(1000); - MockTcpClientEventListener listener = new MockTcpClientEventListener(); - clientRule.getClient().subscribe(listener); - int number_of_iterations = 10; // 300 - int numberOfRequests = 2; // 10 - for(int j = 0; j < number_of_iterations; j++) { - List> results = new ArrayList<>(); - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - for (int i = 0; i < numberOfRequests; i++) { - results.add( - fromCallable((Func0>) clientRule::connectWithCheck) - .flatMap((Func1, Observable>) connection -> - connection.writeStringAndFlushOnEach(just("Hello")) - .toCompletable() - .toObservable() - .concatWith(connection.getInput()) - .take(1) - .single() - .map(byteBuf -> { - try { - byte[] bytes = new byte[byteBuf.readableBytes()]; - byteBuf.readBytes(bytes); - return new String(bytes); - } finally { - byteBuf.release(); - } - }).doOnError(throwable -> { - Assert.fail("Did not expect exception: " + throwable.getMessage()); - throwable.printStackTrace(); - }))); - } - AssertableSubscriber test = Observable.merge(results).test(); - test.awaitTerminalEvent(); - test.assertNoErrors(); - } - } - - /** - * - * Load test to prove concurrency issues mainly seen on heavy load. - * - */ - @Test - public void assertPermitsAreReleasedWhenMergingObservablesWithExceptions() { - clientRule.startServer(10, true); - MockTcpClientEventListener listener = new MockTcpClientEventListener(); - clientRule.getClient().subscribe(listener); - int number_of_iterations = 1; - int numberOfRequests = 3; - makeRequests(number_of_iterations, numberOfRequests); - sleep(clientRule.getPoolConfig().getMaxIdleTimeMillis()); - assertThat("Permits should be 10", - clientRule.getPoolConfig().getPoolLimitDeterminationStrategy().getAvailablePermits(), equalTo(10)); - } - - private void sleep(long i) { - try { - Thread.sleep(i); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - private void makeRequests(int number_of_iterations, int numberOfRequests) { - for (int j = 0; j < number_of_iterations; j++) { - sleep(100); - List> results = new ArrayList<>(); - //Just giving the client some time to recover - sleep(100); - for (int i = 0; i < numberOfRequests; i++) { - results.add( - fromCallable((Func0>) clientRule::connect) - .flatMap((Func1, Observable>) connection -> - connection.writeStringAndFlushOnEach(just("Hello")) - .toCompletable() - .toObservable() - .concatWith(connection.getInput()) - .take(1) - .single() - .map((Func1) byteBuf -> { - try { - byte[] bytes = new byte[byteBuf.readableBytes()]; - byteBuf.readBytes(bytes); - return new String(bytes); - } finally { - byteBuf.release(); - } - }))); - } - AssertableSubscriber test = Observable.merge(results).test(); - test.awaitTerminalEvent(); - } - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/TcpClientImplTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/TcpClientImplTest.java deleted file mode 100644 index 46204ef..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/TcpClientImplTest.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client; - -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.handler.logging.LogLevel; -import io.netty.util.concurrent.EventExecutorGroup; -import io.reactivex.netty.client.ClientState; -import io.reactivex.netty.protocol.tcp.client.events.TcpClientEventPublisher; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Answers; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import rx.functions.Action1; -import rx.functions.Func0; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; -import static org.mockito.Mockito.*; - -@RunWith(MockitoJUnitRunner.class) -public class TcpClientImplTest { - - @Mock(answer = Answers.RETURNS_MOCKS) - private ClientState state; - - @Test(timeout = 60000) - public void testChannelOption() throws Exception { - TcpClientImpl client = TcpClientImpl._create(state, new TcpClientEventPublisher()); - ClientState state = client.getClientState(); - TcpClientImpl newClient = - (TcpClientImpl) client.channelOption(ChannelOption.AUTO_READ, true); - - assertDeepClientCopy(client, newClient); - - verify(state).channelOption(ChannelOption.AUTO_READ, true); - } - - @Test(timeout = 60000) - public void testAddChannelHandlerFirst() throws Exception { - TcpClientImpl client = TcpClientImpl._create(state, new TcpClientEventPublisher()); - ClientState state = client.getClientState(); - Func0 factory = newHandlerFactory(); - TcpClientImpl newClient = - (TcpClientImpl) client.addChannelHandlerFirst("handler", factory); - - assertDeepClientCopy(client, newClient); - - verify(state).addChannelHandlerFirst("handler", factory); - } - - @Test(timeout = 60000) - public void testAddChannelHandlerFirstWithExecutor() throws Exception { - TcpClientImpl client = TcpClientImpl._create(state, new TcpClientEventPublisher()); - ClientState state = client.getClientState(); - Func0 factory = newHandlerFactory(); - EventExecutorGroup group = new NioEventLoopGroup(); - TcpClientImpl newClient = - (TcpClientImpl) client.addChannelHandlerFirst(group, "handler", factory); - - assertDeepClientCopy(client, newClient); - - verify(state).addChannelHandlerFirst(group, "handler", factory); - } - - @Test(timeout = 60000) - public void testAddChannelHandlerLast() throws Exception { - TcpClientImpl client = TcpClientImpl._create(state, new TcpClientEventPublisher()); - ClientState state = client.getClientState(); - Func0 factory = newHandlerFactory(); - TcpClientImpl newClient = - (TcpClientImpl) client.addChannelHandlerLast("handler", factory); - - assertDeepClientCopy(client, newClient); - - verify(state).addChannelHandlerLast("handler", factory); - - } - - @Test(timeout = 60000) - public void testAddChannelHandlerLastWithExecutor() throws Exception { - TcpClientImpl client = TcpClientImpl._create(state, new TcpClientEventPublisher()); - ClientState state = client.getClientState(); - Func0 factory = newHandlerFactory(); - EventExecutorGroup group = new NioEventLoopGroup(); - TcpClientImpl newClient = - (TcpClientImpl) client.addChannelHandlerLast(group, "handler", factory); - - assertDeepClientCopy(client, newClient); - - verify(state).addChannelHandlerLast(group, "handler", factory); - } - - @Test(timeout = 60000) - public void testAddChannelHandlerBefore() throws Exception { - TcpClientImpl client = TcpClientImpl._create(state, new TcpClientEventPublisher()); - ClientState state = client.getClientState(); - Func0 factory = newHandlerFactory(); - TcpClientImpl newClient = - (TcpClientImpl) client.addChannelHandlerBefore("base", "handler", - factory); - - assertDeepClientCopy(client, newClient); - - verify(state).addChannelHandlerBefore("base", "handler", factory); - } - - @Test(timeout = 60000) - public void testAddChannelHandlerBeforeWithExecutor() throws Exception { - TcpClientImpl client = TcpClientImpl._create(state, new TcpClientEventPublisher()); - ClientState state = client.getClientState(); - Func0 factory = newHandlerFactory(); - EventExecutorGroup group = new NioEventLoopGroup(); - TcpClientImpl newClient = - (TcpClientImpl) client.addChannelHandlerBefore(group, "base", "handler", - factory); - - assertDeepClientCopy(client, newClient); - - verify(state).addChannelHandlerBefore(group, "base", "handler", factory); - - } - - @Test(timeout = 60000) - public void testAddChannelHandlerAfter() throws Exception { - TcpClientImpl client = TcpClientImpl._create(state, new TcpClientEventPublisher()); - ClientState state = client.getClientState(); - Func0 factory = newHandlerFactory(); - TcpClientImpl newClient = - (TcpClientImpl) client.addChannelHandlerAfter("base", "handler", - factory); - - assertDeepClientCopy(client, newClient); - - verify(state).addChannelHandlerAfter("base", "handler", factory); - } - - @Test(timeout = 60000) - public void testAddChannelHandlerAfterWithExecutor() throws Exception { - TcpClientImpl client = TcpClientImpl._create(state, new TcpClientEventPublisher()); - ClientState state = client.getClientState(); - Func0 factory = newHandlerFactory(); - EventExecutorGroup group = new NioEventLoopGroup(); - TcpClientImpl newClient = - (TcpClientImpl) client.addChannelHandlerAfter(group, "base", "handler", - factory); - - assertDeepClientCopy(client, newClient); - - verify(state).addChannelHandlerAfter(group, "base", "handler", factory); - - } - - @Test(timeout = 60000) - public void testPipelineConfigurator() throws Exception { - TcpClientImpl client = TcpClientImpl._create(state, new TcpClientEventPublisher()); - ClientState state = client.getClientState(); - Action1 configurator = new Action1() { - @Override - public void call(ChannelPipeline pipeline) { - } - }; - TcpClientImpl newClient = - (TcpClientImpl) client.pipelineConfigurator(configurator); - - assertDeepClientCopy(client, newClient); - - verify(state).pipelineConfigurator(configurator); - } - - @Test(timeout = 60000) - public void testEnableWireLogging() throws Exception { - TcpClientImpl client = TcpClientImpl._create(state, new TcpClientEventPublisher()); - ClientState state = client.getClientState(); - TcpClientImpl newClient = - (TcpClientImpl) client.enableWireLogging("test", LogLevel.DEBUG); - - assertDeepClientCopy(client, newClient); - - verify(state).enableWireLogging("test", LogLevel.DEBUG); - } - - private static void assertDeepClientCopy(TcpClientImpl client, - TcpClientImpl newClient) { - assertThat("Client was not copied.", newClient, is(not(client))); - assertThat("Client state was not copied.", newClient.getClientState(), - is(not(client.getClientState()))); - } - - private static Func0 newHandlerFactory() { - return new Func0() { - @Override - public ChannelHandler call() { - return new ChannelDuplexHandler(); - } - }; - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/TcpClientRule.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/TcpClientRule.java deleted file mode 100644 index 36b15f9..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/TcpClientRule.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client; - -import io.netty.buffer.ByteBuf; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.client.Host; -import io.reactivex.netty.client.pool.PoolConfig; -import io.reactivex.netty.client.pool.PooledConnection; -import io.reactivex.netty.client.pool.SingleHostPoolingProviderFactory; -import io.reactivex.netty.protocol.tcp.server.ConnectionHandler; -import io.reactivex.netty.protocol.tcp.server.TcpServer; -import org.junit.Assert; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.Observable; -import rx.Observer; -import rx.functions.Func0; -import rx.observers.TestSubscriber; - -import java.net.InetSocketAddress; -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; - -public class TcpClientRule extends ExternalResource { - - private TcpServer server; - private TcpClient client; - private PoolConfig poolConfig; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - server = TcpServer.newServer(); - base.evaluate(); - } - }; - } - - public void startServer(int maxConnections) { - startServer(maxConnections, false); - } - - public void startServer(int maxConnections, final boolean failing) { - server.start(new ConnectionHandler() { - @Override - public Observable handle(Connection newConnection) { - if(failing) { - throw new RuntimeException("exception"); - } else { - return newConnection.writeAndFlushOnEach(newConnection.getInput()); - } - } - }); - createClient(maxConnections); - } - - public void startServer(ConnectionHandler handler, int maxConnections) { - server.start(handler); - createClient(maxConnections); - } - - public PooledConnection connect() { - - TestSubscriber> cSub = new TestSubscriber<>(); - client.createConnectionRequest().subscribe(cSub); - - cSub.awaitTerminalEvent(); - - cSub.assertNoErrors(); - - assertThat("No connection received.", cSub.getOnNextEvents(), hasSize(1)); - - return (PooledConnection) cSub.getOnNextEvents().get(0); - } - - public PooledConnection connectWithCheck() { - - final AtomicBoolean gotOnNext = new AtomicBoolean(false); - - Observable> got_no_connection = client.createConnectionRequest() - .doOnEach(new Observer>() { - @Override - public void onCompleted() { - if(!gotOnNext.get()) { - //A PooledConnection could sometimes send onCompleted before the onNext event occurred. - Assert.fail("Should not get onCompletedBefore onNext"); - } - } - - @Override - public void onError(Throwable e) { - } - - @Override - public void onNext(Connection byteBufByteBufConnection) { - gotOnNext.set(true); - } - }) - .switchIfEmpty(Observable.defer(new Func0>>() { - @Override - public Observable> call() { - return Observable.empty(); - } - })); - - TestSubscriber> cSub = new TestSubscriber<>(); - got_no_connection.subscribe(cSub); - - cSub.awaitTerminalEvent(); - - cSub.assertNoErrors(); - - assertThat("No connection received.", cSub.getOnNextEvents(), hasSize(1)); - - return (PooledConnection) cSub.getOnNextEvents().get(0); - } - - private void createClient(final int maxConnections) { - InetSocketAddress serverAddr = new InetSocketAddress("127.0.0.1", server.getServerPort()); - poolConfig = new PoolConfig().maxConnections(maxConnections); - SingleHostPoolingProviderFactory bounded = SingleHostPoolingProviderFactory.create(poolConfig); - client = TcpClient.newClient(bounded, Observable.just(new Host(serverAddr))); - } - - public TcpServer getServer() { - return server; - } - - public TcpClient getClient() { - return client; - } - - public PoolConfig getPoolConfig() { - return poolConfig; - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/events/TcpClientEventPublisherTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/events/TcpClientEventPublisherTest.java deleted file mode 100644 index c664c3e..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/events/TcpClientEventPublisherTest.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.client.events; - -import io.reactivex.netty.protocol.tcp.client.MockTcpClientEventListener; -import io.reactivex.netty.test.util.MockClientEventListener.ClientEvent; -import io.reactivex.netty.test.util.MockConnectionEventListener.Event; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class TcpClientEventPublisherTest { - - @Rule - public PublisherRule rule = new PublisherRule(); - - @Test(timeout = 60000) - public void testOnConnectStart() throws Exception { - rule.publisher.onConnectStart(); - - rule.listener.assertMethodsCalled(ClientEvent.ConnectStart); - } - - @Test(timeout = 60000) - public void testOnConnectSuccess() throws Exception { - rule.publisher.onConnectSuccess(1, TimeUnit.MILLISECONDS); - - rule.listener.assertMethodsCalled(ClientEvent.ConnectSuccess); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testOnConnectFailed() throws Exception { - final Throwable expected = new NullPointerException(); - - rule.publisher.onConnectFailed(1, TimeUnit.MILLISECONDS, expected); - - rule.listener.assertMethodsCalled(ClientEvent.ConnectFailed); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - assertThat("Listener not called with error.", rule.listener.getRecievedError(), is(expected)); - } - - @Test(timeout = 60000) - public void testOnPoolReleaseStart() throws Exception { - rule.publisher.onPoolReleaseStart(); - - rule.listener.assertMethodsCalled(ClientEvent.ReleaseStart); - } - - @Test(timeout = 60000) - public void testOnPoolReleaseSuccess() throws Exception { - rule.publisher.onPoolReleaseSuccess(1, TimeUnit.MILLISECONDS); - - rule.listener.assertMethodsCalled(ClientEvent.ReleaseSuccess); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testOnPoolReleaseFailed() throws Exception { - final Throwable expected = new NullPointerException(); - - rule.publisher.onPoolReleaseFailed(1, TimeUnit.MILLISECONDS, expected); - - rule.listener.assertMethodsCalled(ClientEvent.ReleaseFailed); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - assertThat("Listener not called with error.", rule.listener.getRecievedError(), is(expected)); - } - - @Test(timeout = 60000) - public void testOnPooledConnectionEviction() throws Exception { - rule.publisher.onPooledConnectionEviction(); - - rule.listener.assertMethodsCalled(ClientEvent.Eviction); - } - - @Test(timeout = 60000) - public void testOnPooledConnectionReuse() throws Exception { - rule.publisher.onPooledConnectionReuse(); - - rule.listener.assertMethodsCalled(ClientEvent.Reuse); - } - - @Test(timeout = 60000) - public void testOnPoolAcquireStart() throws Exception { - rule.publisher.onPoolAcquireStart(); - - rule.listener.assertMethodsCalled(ClientEvent.AcquireStart); - } - - @Test(timeout = 60000) - public void testOnPoolAcquireSuccess() throws Exception { - rule.publisher.onPoolAcquireSuccess(1, TimeUnit.MILLISECONDS); - - rule.listener.assertMethodsCalled(ClientEvent.AcquireSuccess); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testOnPoolAcquireFailed() throws Exception { - final Throwable expected = new NullPointerException(); - - rule.publisher.onPoolAcquireFailed(1, TimeUnit.MILLISECONDS, expected); - - rule.listener.assertMethodsCalled(ClientEvent.AcquireFailed); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(TimeUnit.MILLISECONDS)); - assertThat("Listener not called with error.", rule.listener.getRecievedError(), is(expected)); - } - - @Test(timeout = 60000) - public void testOnByteRead() throws Exception { - rule.publisher.onByteRead(1); - - rule.listener.assertMethodsCalled(Event.BytesRead); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnByteWritten() throws Exception { - rule.publisher.onByteWritten(1); - - rule.listener.assertMethodsCalled(Event.BytesWritten); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnFlushStart() throws Exception { - rule.publisher.onFlushStart(); - - rule.listener.assertMethodsCalled(Event.FlushStart); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnFlushSuccess() throws Exception { - rule.publisher.onFlushComplete(1, TimeUnit.MILLISECONDS); - - rule.listener.assertMethodsCalled(Event.FlushSuccess); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnWriteStart() throws Exception { - rule.publisher.onWriteStart(); - - rule.listener.assertMethodsCalled(Event.WriteStart); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnWriteSuccess() throws Exception { - rule.publisher.onWriteSuccess(1, TimeUnit.MILLISECONDS); - - rule.listener.assertMethodsCalled(Event.WriteSuccess); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnWriteFailed() throws Exception { - rule.publisher.onWriteFailed(1, TimeUnit.MILLISECONDS, new NullPointerException()); - - rule.listener.assertMethodsCalled(Event.WriteFailed); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnConnectionCloseStart() throws Exception { - rule.publisher.onConnectionCloseStart(); - - rule.listener.assertMethodsCalled(Event.CloseStart); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnConnectionCloseSuccess() throws Exception { - rule.publisher.onConnectionCloseSuccess(1, TimeUnit.MILLISECONDS); - - rule.listener.assertMethodsCalled(Event.CloseSuccess); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnConnectionCloseFailed() throws Exception { - rule.publisher.onConnectionCloseFailed(1, TimeUnit.MILLISECONDS, new NullPointerException()); - - rule.listener.assertMethodsCalled(Event.CloseFailed); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testPublishingEnabled() throws Exception { - assertThat("Publishing not enabled.", rule.publisher.publishingEnabled(), is(true)); - } - - @Test(timeout = 60000) - public void testCustomEvent() throws Exception { - rule.publisher.onCustomEvent("Hello"); - rule.listener.assertMethodsCalled(Event.CustomEvent); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testCustomEventWithError() throws Exception { - rule.publisher.onCustomEvent("Hello", new NullPointerException()); - rule.listener.assertMethodsCalled(Event.CustomEventWithError); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testCustomEventWithDuration() throws Exception { - rule.publisher.onCustomEvent("Hello", 1, TimeUnit.MINUTES); - rule.listener.assertMethodsCalled(Event.CustomEventWithDuration); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testCustomEventWithDurationAndError() throws Exception { - rule.publisher.onCustomEvent("Hello", 1, TimeUnit.MINUTES, new NullPointerException()); - rule.listener.assertMethodsCalled(Event.CustomEventWithDurationAndError); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testCopy() throws Exception { - TcpClientEventPublisher copy = rule.publisher.copy(); - - assertThat("Publisher not copied.", copy, is(not(sameInstance(rule.publisher)))); - assertThat("Listeners not copied.", copy.getListeners(), is(not(sameInstance(rule.publisher.getListeners())))); - } - - public static class PublisherRule extends ExternalResource { - - private MockTcpClientEventListener listener; - private TcpClientEventPublisher publisher; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - listener = new MockTcpClientEventListener(); - publisher = new TcpClientEventPublisher(); - publisher.subscribe(listener); - base.evaluate(); - } - }; - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/events/TcpClientEventsTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/events/TcpClientEventsTest.java deleted file mode 100644 index 186e6c8..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/client/events/TcpClientEventsTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.reactivex.netty.protocol.tcp.client.events; - -import io.netty.buffer.ByteBuf; -import io.reactivex.netty.client.pool.PooledConnection; -import io.reactivex.netty.protocol.tcp.client.MockTcpClientEventListener; -import io.reactivex.netty.protocol.tcp.client.TcpClientRule; -import io.reactivex.netty.test.util.MockClientEventListener.ClientEvent; -import io.reactivex.netty.test.util.MockConnectionEventListener.Event; -import org.junit.Rule; -import org.junit.Test; -import rx.Observable; -import rx.observers.TestSubscriber; - -public class TcpClientEventsTest { - - @Rule - public final TcpClientRule clientRule = new TcpClientRule(); - - @Test(timeout = 60000) - public void testEventsPublished() throws Exception { - MockTcpClientEventListener listener = sendRequests(); - - listener.assertMethodCalled(ClientEvent.AcquireStart); - listener.assertMethodCalled(ClientEvent.AcquireSuccess); - listener.assertMethodCalled(ClientEvent.ConnectStart); - listener.assertMethodCalled(ClientEvent.ConnectSuccess); - listener.assertMethodCalled(Event.WriteStart); - listener.assertMethodCalled(Event.WriteSuccess); - listener.assertMethodCalled(Event.FlushStart); - listener.assertMethodCalled(Event.FlushSuccess); - listener.assertMethodCalled(Event.BytesRead); - } - - protected MockTcpClientEventListener sendRequests() { - clientRule.startServer(10); - MockTcpClientEventListener listener = new MockTcpClientEventListener(); - clientRule.getClient().subscribe(listener); - PooledConnection connection = clientRule.connect(); - TestSubscriber testSubscriber = new TestSubscriber<>(); - connection.writeStringAndFlushOnEach(Observable.just("Hello")) - .toCompletable() - .toObservable() - .concatWith(connection.getInput()) - .take(1) - .subscribe(testSubscriber); - - testSubscriber.awaitTerminalEvent(); - testSubscriber.assertNoErrors(); - return listener; - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/server/UnexpectedConnectionHandlerErrorsTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/server/UnexpectedConnectionHandlerErrorsTest.java deleted file mode 100644 index 3fd87c0..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/server/UnexpectedConnectionHandlerErrorsTest.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.server; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelOption; -import io.netty.handler.logging.LogLevel; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.protocol.tcp.client.TcpClient; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.Observable; -import rx.observers.TestSubscriber; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class UnexpectedConnectionHandlerErrorsTest { - - @Rule - public final ErrorRule rule = new ErrorRule(); - - @Test(timeout = 60000) - public void testHandlerReturnsNull() throws Exception { - rule.server.start(new ConnectionHandler() { - @Override - public Observable handle(Connection newConnection) { - return null; - } - }); - - connectAndWaitForClose(); - } - - @Test(timeout = 60000) - public void testHandlerReturnsError() throws Exception { - rule.server.start(new ConnectionHandler() { - @Override - public Observable handle(Connection newConnection) { - return Observable.error(new IllegalStateException("Deliberate Exception")); - } - }); - - connectAndWaitForClose(); - } - - @Test(timeout = 60000) - public void testHandlerThrowsError() throws Exception { - rule.server.start(new ConnectionHandler() { - @Override - public Observable handle(Connection newConnection) { - return Observable.error(new IllegalStateException("Deliberate Exception")); - } - }); - - connectAndWaitForClose(); - } - - private void connectAndWaitForClose() { - Connection connection = rule.connectToServer(); - - TestSubscriber subscriber = new TestSubscriber<>(); - connection.closeListener().subscribe(subscriber); - - subscriber.awaitTerminalEvent(); - subscriber.assertNoErrors(); - } - - public static class ErrorRule extends ExternalResource { - - private TcpServer server; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - server = TcpServer.newServer(0).enableWireLogging("test", LogLevel.ERROR); - base.evaluate(); - } - }; - } - - public Connection connectToServer() { - final TestSubscriber> subscriber = new TestSubscriber<>(); - - TcpClient.newClient("127.0.0.1", server.getServerPort()) - .channelOption(ChannelOption.AUTO_READ, true) /*Else nothing is read from the channel even close*/ - .enableWireLogging("test", LogLevel.ERROR) - .createConnectionRequest() - .subscribe(subscriber); - - subscriber.awaitTerminalEvent(); - subscriber.assertNoErrors(); - assertThat("No connection available.", subscriber.getOnNextEvents(), hasSize(1)); - - return subscriber.getOnNextEvents().get(0); - } - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/server/events/TcpServerEventPublisherTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/server/events/TcpServerEventPublisherTest.java deleted file mode 100644 index f9b8bd1..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/protocol/tcp/server/events/TcpServerEventPublisherTest.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.protocol.tcp.server.events; - -import io.reactivex.netty.test.util.MockConnectionEventListener.Event; -import io.reactivex.netty.test.util.MockTcpServerEventListener; -import io.reactivex.netty.test.util.MockTcpServerEventListener.ServerEvent; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import static java.util.concurrent.TimeUnit.*; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class TcpServerEventPublisherTest { - - @Rule - public final PublisherRule rule = new PublisherRule(); - - @Test(timeout = 60000) - public void testOnNewClientConnected() throws Exception { - rule.publisher.onNewClientConnected(); - - rule.listener.assertMethodsCalled(ServerEvent.NewClient); - } - - @Test(timeout = 60000) - public void testOnConnectionHandlingStart() throws Exception { - rule.publisher.onConnectionHandlingStart(1, MILLISECONDS); - - rule.listener.assertMethodsCalled(ServerEvent.HandlingStart); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testOnConnectionHandlingSuccess() throws Exception { - rule.publisher.onConnectionHandlingSuccess(1, MILLISECONDS); - - rule.listener.assertMethodsCalled(ServerEvent.HandlingSuccess); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(MILLISECONDS)); - } - - @Test(timeout = 60000) - public void testOnConnectionHandlingFailed() throws Exception { - final Throwable expected = new NullPointerException(); - - rule.publisher.onConnectionHandlingFailed(1, MILLISECONDS, expected); - - rule.listener.assertMethodsCalled(ServerEvent.HandlingFailed); - - assertThat("Listener not called with duration.", rule.listener.getDuration(), is(1L)); - assertThat("Listener not called with time unit.", rule.listener.getTimeUnit(), is(MILLISECONDS)); - assertThat("Listener not called with error.", rule.listener.getRecievedError(), is(expected)); - } - - @Test(timeout = 60000) - public void testOnByteRead() throws Exception { - rule.publisher.onByteRead(1); - - rule.listener.getConnDelegate().assertMethodsCalled(Event.BytesRead); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnByteWritten() throws Exception { - rule.publisher.onByteWritten(1); - - rule.listener.getConnDelegate().assertMethodsCalled(Event.BytesWritten); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnFlushStart() throws Exception { - rule.publisher.onFlushStart(); - - rule.listener.getConnDelegate().assertMethodsCalled(Event.FlushStart); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnFlushSuccess() throws Exception { - rule.publisher.onFlushComplete(1, MILLISECONDS); - - rule.listener.getConnDelegate().assertMethodsCalled(Event.FlushSuccess); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnWriteStart() throws Exception { - rule.publisher.onWriteStart(); - - rule.listener.getConnDelegate().assertMethodsCalled(Event.WriteStart); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnWriteSuccess() throws Exception { - rule.publisher.onWriteSuccess(1, MILLISECONDS); - - rule.listener.getConnDelegate().assertMethodsCalled(Event.WriteSuccess); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnWriteFailed() throws Exception { - rule.publisher.onWriteFailed(1, MILLISECONDS, new NullPointerException()); - - rule.listener.getConnDelegate().assertMethodsCalled(Event.WriteFailed); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnConnectionCloseStart() throws Exception { - rule.publisher.onConnectionCloseStart(); - - rule.listener.getConnDelegate().assertMethodsCalled(Event.CloseStart); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnConnectionCloseSuccess() throws Exception { - rule.publisher.onConnectionCloseSuccess(1, MILLISECONDS); - - rule.listener.getConnDelegate().assertMethodsCalled(Event.CloseSuccess); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnConnectionCloseFailed() throws Exception { - rule.publisher.onConnectionCloseFailed(1, MILLISECONDS, new NullPointerException()); - - rule.listener.getConnDelegate().assertMethodsCalled(Event.CloseFailed); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnCustomEvent() throws Exception { - rule.publisher.onCustomEvent("Hello"); - rule.listener.getConnDelegate().assertMethodsCalled(Event.CustomEvent); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnCustomEventWithError() throws Exception { - rule.publisher.onCustomEvent("Hello", new NullPointerException()); - rule.listener.getConnDelegate().assertMethodsCalled(Event.CustomEventWithError); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnCustomEventWithDuration() throws Exception { - rule.publisher.onCustomEvent("Hello", 1, MINUTES); - rule.listener.getConnDelegate().assertMethodsCalled(Event.CustomEventWithDuration); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testOnCustomEventWithDurationError() throws Exception { - rule.publisher.onCustomEvent("Hello", 1, MINUTES, new NullPointerException()); - rule.listener.getConnDelegate().assertMethodsCalled(Event.CustomEventWithDurationAndError); // Test for Connection publisher should verify rest - } - - @Test(timeout = 60000) - public void testPublishingEnabled() throws Exception { - assertThat("Publishing not enabled.", rule.publisher.publishingEnabled(), is(true)); - } - - @Test(timeout = 60000) - public void testCopy() throws Exception { - final TcpServerEventPublisher copy = rule.publisher.copy(); - - assertThat("Publisher not copied.", copy, is(not(sameInstance(rule.publisher)))); - assertThat("Listeners not copied.", copy.getListeners(), is(not(sameInstance(rule.publisher.getListeners())))); - assertThat("Listeners not copied.", copy.getConnDelegate(), - is(not(sameInstance(rule.publisher.getConnDelegate())))); - } - - public static class PublisherRule extends ExternalResource { - - private MockTcpServerEventListener listener; - private TcpServerEventPublisher publisher; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - listener = new MockTcpServerEventListener(); - publisher = new TcpServerEventPublisher(); - publisher.subscribe(listener); - base.evaluate(); - } - }; - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/FlushSelector.java b/netty-http-rx/src/test/java/io/reactivex/netty/test/util/FlushSelector.java deleted file mode 100644 index fa36b49..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/FlushSelector.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.test.util; - -import rx.functions.Func1; - -public class FlushSelector implements Func1 { - - private final int flushEvery; - private int count; - - public FlushSelector(int flushEvery) { - this.flushEvery = flushEvery; - } - - @Override - public Boolean call(T o) { - return ++count % flushEvery == 0; - } - - public int getFlushEvery() { - return flushEvery; - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/InboundRequestFeeder.java b/netty-http-rx/src/test/java/io/reactivex/netty/test/util/InboundRequestFeeder.java deleted file mode 100644 index a80c7d6..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/InboundRequestFeeder.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.test.util; - -import io.netty.channel.ChannelConfig; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundHandlerAdapter; -import io.netty.channel.embedded.EmbeddedChannel; - -import java.util.Collections; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * A handler to be added, typically to an {@link EmbeddedChannel} to simulate read from an actual channel. - * Objects can be added to the buffer of this handler via {@link #addToTheFeed(Object...)} which will be sent to the - * pipeline when requested via {@link #read(ChannelHandlerContext)}. - * This handler abides by the contract of netty's backpressure semantics, to only send the number of messages as - * requested by {@link ChannelConfig#getMaxMessagesPerRead()}, followed by - * {@link ChannelHandlerContext#fireChannelReadComplete()} - */ -@Sharable -public class InboundRequestFeeder extends ChannelOutboundHandlerAdapter { - - private final ConcurrentLinkedQueue feed = new ConcurrentLinkedQueue<>(); - private final AtomicInteger readRequestedCount = new AtomicInteger(); - private int pendingReads; - private boolean sending; - private ChannelHandlerContext ctx; - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - ctx.channel().config().setAutoRead(false); - } - - @Override - public void read(ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - - readRequestedCount.incrementAndGet(); - pendingReads++; - - _sendMessages(ctx); - } - - public void addToTheFeed(Object... msgs) { - if (null != msgs && msgs.length > 0) { - Collections.addAll(feed, msgs); - if (pendingReads > 0) { - _sendMessages(ctx); - } - } - } - - private void _sendMessages(ChannelHandlerContext ctx) { - if (sending) { - return; - } - sending = true; - int sentInThisIteration = 0; - while (true) { - Object next = feed.poll(); - if (null == next) { - break; - } - sentInThisIteration++; - ctx.fireChannelRead(next); - - if (sentInThisIteration >= ctx.channel().config().getMaxMessagesPerRead()) { - sentInThisIteration = 0; - pendingReads--; - ctx.fireChannelReadComplete(); - } - - if (pendingReads <= 0) { - break; - } - } - - sending = false; - } - - public int getReadRequestedCount() { - return readRequestedCount.get(); - } - - public int resetReadRequested() { - return readRequestedCount.getAndSet(0); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockClientEventListener.java b/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockClientEventListener.java deleted file mode 100644 index c55245e..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockClientEventListener.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.test.util; - -import io.reactivex.netty.client.events.ClientEventListener; -import io.reactivex.netty.test.util.MockConnectionEventListener.Event; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.TimeUnit; - -public class MockClientEventListener extends ClientEventListener { - - public enum ClientEvent { - ConnectStart, ConnectSuccess, ConnectFailed, ReleaseStart, ReleaseSuccess, ReleaseFailed, Eviction, Reuse, - AcquireStart, AcquireSuccess, AcquireFailed - } - - private final List methodsCalled = new ArrayList<>(); - private long duration; - private TimeUnit timeUnit; - private Throwable recievedError; - - private final MockConnectionEventListener delegate = new MockConnectionEventListener(); - - @Override - public void onConnectStart() { - methodsCalled.add(ClientEvent.ConnectStart); - } - - @Override - public void onConnectSuccess(long duration, TimeUnit timeUnit) { - this.duration = duration; - this.timeUnit = timeUnit; - methodsCalled.add(ClientEvent.ConnectSuccess); - } - - @Override - public void onConnectFailed(long duration, TimeUnit timeUnit, Throwable recievedError) { - methodsCalled.add(ClientEvent.ConnectFailed); - this.duration = duration; - this.timeUnit = timeUnit; - this.recievedError = recievedError; - } - - @Override - public void onPoolReleaseStart() { - methodsCalled.add(ClientEvent.ReleaseStart); - } - - @Override - public void onPoolReleaseSuccess(long duration, TimeUnit timeUnit) { - methodsCalled.add(ClientEvent.ReleaseSuccess); - this.duration = duration; - this.timeUnit = timeUnit; - } - - @Override - public void onPoolReleaseFailed(long duration, TimeUnit timeUnit, Throwable recievedError) { - methodsCalled.add(ClientEvent.ReleaseFailed); - this.duration = duration; - this.timeUnit = timeUnit; - this.recievedError = recievedError; - } - - @Override - public void onPooledConnectionEviction() { - methodsCalled.add(ClientEvent.Eviction); - } - - @Override - public void onPooledConnectionReuse() { - methodsCalled.add(ClientEvent.Reuse); - } - - @Override - public void onPoolAcquireStart() { - methodsCalled.add(ClientEvent.AcquireStart); - } - - @Override - public void onPoolAcquireSuccess(long duration, TimeUnit timeUnit) { - methodsCalled.add(ClientEvent.AcquireSuccess); - this.duration = duration; - this.timeUnit = timeUnit; - } - - @Override - public void onPoolAcquireFailed(long duration, TimeUnit timeUnit, Throwable recievedError) { - this.duration = duration; - this.timeUnit = timeUnit; - this.recievedError = recievedError; - methodsCalled.add(ClientEvent.AcquireFailed); - } - - @Override - public void onConnectionCloseFailed(long duration, TimeUnit timeUnit, - Throwable recievedError) { - delegate.onConnectionCloseFailed(duration, timeUnit, recievedError); - } - - @Override - public void onConnectionCloseSuccess(long duration, TimeUnit timeUnit) { - delegate.onConnectionCloseSuccess(duration, timeUnit); - } - - @Override - public void onConnectionCloseStart() { - delegate.onConnectionCloseStart(); - } - - @Override - public void onWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - delegate.onWriteFailed(duration, timeUnit, throwable); - } - - @Override - public void onWriteSuccess(long duration, TimeUnit timeUnit) { - delegate.onWriteSuccess(duration, timeUnit); - } - - @Override - public void onWriteStart() { - delegate.onWriteStart(); - } - - @Override - public void onFlushComplete(long duration, TimeUnit timeUnit) { - delegate.onFlushComplete(duration, timeUnit); - } - - @Override - public void onFlushStart() { - delegate.onFlushStart(); - } - - @Override - public void onByteRead(long bytesRead) { - delegate.onByteRead(bytesRead); - } - - @Override - public void onByteWritten(long bytesWritten) { - delegate.onByteWritten(bytesWritten); - } - - @Override - public void onCustomEvent(Object event) { - delegate.onCustomEvent(event); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { - delegate.onCustomEvent(event, duration, timeUnit); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { - delegate.onCustomEvent(event, duration, timeUnit, throwable); - } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { - delegate.onCustomEvent(event, throwable); - } - - @Override - public void onCompleted() { - delegate.onCompleted(); - } - - public void assertMethodCalled(Event event) { - delegate.assertMethodCalled(event); - } - - public void assertMethodsCalled(Event... events) { - delegate.assertMethodsCalled(events); - } - - public void assertMethodCalled(ClientEvent event) { - if (!methodsCalled.contains(event)) { - throw new AssertionError("Method " + event + " not called. Methods called: " + methodsCalled); - } - } - - public void assertMethodsCalled(ClientEvent... events) { - if (methodsCalled.size() != events.length) { - throw new AssertionError("Unexpected methods called count. Methods called: " + methodsCalled.size() - + ". Expected: " + events.length); - } - - if (!methodsCalled.containsAll(Arrays.asList(events))) { - throw new AssertionError("Unexpected methods called count. Methods called: " + methodsCalled - + ". Expected: " + Arrays.toString(events)); - } - } - - public long getDuration() { - return duration; - } - - public TimeUnit getTimeUnit() { - return timeUnit; - } - - public Throwable getRecievedError() { - return recievedError; - } - - @Override - public String toString() { - return "MockClientEventListener{" + - "methodsCalled=" + methodsCalled + - ", duration=" + duration + - ", timeUnit=" + timeUnit + - ", recievedError=" + recievedError + - ", delegate=" + delegate + - '}'; - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockConnectionEventListener.java b/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockConnectionEventListener.java deleted file mode 100644 index a2ad31e..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockConnectionEventListener.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.test.util; - -import io.reactivex.netty.channel.events.ConnectionEventListener; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.TimeUnit; - -public class MockConnectionEventListener extends ConnectionEventListener { - - public enum Event { - BytesRead, BytesWritten, FlushStart, FlushSuccess, WriteStart, WriteSuccess, WriteFailed, CloseStart, - CloseSuccess, CloseFailed, CustomEvent, CustomEventWithDuration, CustomEventWithDurationAndError, - CustomEventWithError, Complete - } - - private final List methodsCalled = new ArrayList<>(); - private long bytesRead; - private long duration; - private TimeUnit timeUnit; - private long bytesWritten; - private Throwable recievedError; - private Object customeEvent; - - @Override - public void onByteRead(long bytesRead) { - methodsCalled.add(Event.BytesRead); - this.bytesRead = bytesRead; - } - - @Override - public void onByteWritten(long bytesWritten) { - methodsCalled.add(Event.BytesWritten); - this.bytesWritten = bytesWritten; - } - - @Override - public void onFlushStart() { - methodsCalled.add(Event.FlushStart); - } - - @Override - public void onFlushComplete(long duration, TimeUnit timeUnit) { - methodsCalled.add(Event.FlushSuccess); - this.duration = duration; - this.timeUnit = timeUnit; - } - - @Override - public void onWriteStart() { - methodsCalled.add(Event.WriteStart); - } - - @Override - public void onWriteSuccess(long duration, TimeUnit timeUnit) { - methodsCalled.add(Event.WriteSuccess); - this.duration = duration; - this.timeUnit = timeUnit; - } - - @Override - public void onWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - methodsCalled.add(Event.WriteFailed); - this.duration = duration; - this.timeUnit = timeUnit; - recievedError = throwable; - } - - @Override - public void onConnectionCloseStart() { - methodsCalled.add(Event.CloseStart); - } - - @Override - public void onConnectionCloseSuccess(long duration, TimeUnit timeUnit) { - methodsCalled.add(Event.CloseSuccess); - this.duration = duration; - this.timeUnit = timeUnit; - } - - @Override - public void onConnectionCloseFailed(long duration, TimeUnit timeUnit, Throwable recievedError) { - methodsCalled.add(Event.CloseFailed); - this.duration = duration; - this.timeUnit = timeUnit; - this.recievedError = recievedError; - } - - @Override - public void onCustomEvent(Object event) { - methodsCalled.add(Event.CustomEvent); - customeEvent = event; - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { - methodsCalled.add(Event.CustomEventWithDuration); - customeEvent = event; - this.duration = duration; - this.timeUnit = timeUnit; - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { - methodsCalled.add(Event.CustomEventWithDurationAndError); - customeEvent = event; - this.duration = duration; - this.timeUnit = timeUnit; - recievedError = throwable; - } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { - methodsCalled.add(Event.CustomEventWithError); - customeEvent = event; - recievedError = throwable; - } - - @Override - public void onCompleted() { - methodsCalled.add(Event.Complete); - } - - public void assertMethodCalled(Event event) { - if (!methodsCalled.contains(event)) { - throw new AssertionError("Method " + event + " not called. Methods called: " + methodsCalled); - } - } - - public void assertMethodsCalled(Event... events) { - if (methodsCalled.size() < events.length) { - throw new AssertionError("Unexpected methods called count. Methods called: " + methodsCalled - + ". Expected: " + Arrays.toString(events)); - } - - if (!methodsCalled.containsAll(Arrays.asList(events))) { - throw new AssertionError("Unexpected methods called count. Methods called: " + methodsCalled - + ". Expected: " + Arrays.toString(events)); - } - } - - public List getMethodsCalled() { - return methodsCalled; - } - - public long getBytesRead() { - return bytesRead; - } - - public long getDuration() { - return duration; - } - - public TimeUnit getTimeUnit() { - return timeUnit; - } - - public long getBytesWritten() { - return bytesWritten; - } - - public Throwable getRecievedError() { - return recievedError; - } - - public Object getCustomEvent() { - return customeEvent; - } - - @Override - public String toString() { - return "MockConnectionEventListener{" + - "methodsCalled=" + methodsCalled + - ", bytesRead=" + bytesRead + - ", duration=" + duration + - ", timeUnit=" + timeUnit + - ", bytesWritten=" + bytesWritten + - ", recievedError=" + recievedError + - ", customeEvent=" + customeEvent + - '}'; - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockEventListener.java b/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockEventListener.java deleted file mode 100644 index a28c299..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockEventListener.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.test.util; - -import io.reactivex.netty.events.EventListener; - -import java.util.concurrent.TimeUnit; - -public class MockEventListener implements EventListener { - - private int onCompletedCount; - private int eventInvocationCount; - private final boolean raiseErrorOnAllInvocations; - private long duration; - private TimeUnit timeUnit; - private Throwable recievedError; - private String arg; - private Object customEvent; - - public MockEventListener() { - this(false); - } - - public MockEventListener(boolean raiseErrorOnAllInvocations) { - this.raiseErrorOnAllInvocations = raiseErrorOnAllInvocations; - } - - public void anEvent() { - eventInvocationCount++; - if (raiseErrorOnAllInvocations) { - throw new IllegalStateException("Deliberate exception."); - } - } - - public void anEventWithArg(String arg) { - eventInvocationCount++; - this.arg = arg; - if (raiseErrorOnAllInvocations) { - throw new IllegalStateException("Deliberate exception."); - } - } - - public void anEventWithDuration(long duration, TimeUnit timeUnit) { - this.duration = duration; - this.timeUnit = timeUnit; - eventInvocationCount++; - if (raiseErrorOnAllInvocations) { - throw new IllegalStateException("Deliberate exception."); - } - } - - public void anEventWithDurationAndError(long duration, TimeUnit timeUnit, Throwable t) { - this.duration = duration; - this.timeUnit = timeUnit; - recievedError = t; - eventInvocationCount++; - if (raiseErrorOnAllInvocations) { - throw new IllegalStateException("Deliberate exception."); - } - } - - public void anEventWithDurationAndArg(long duration, TimeUnit timeUnit, String arg) { - this.duration = duration; - this.timeUnit = timeUnit; - this.arg = arg; - eventInvocationCount++; - if (raiseErrorOnAllInvocations) { - throw new IllegalStateException("Deliberate exception."); - } - } - - @Override - public void onCompleted() { - onCompletedCount++; - if (raiseErrorOnAllInvocations) { - throw new IllegalStateException("Deliberate exception."); - } - } - - @Override - public void onCustomEvent(Object event) { - customEvent = event; - eventInvocationCount++; - if (raiseErrorOnAllInvocations) { - throw new IllegalStateException("Deliberate exception."); - } - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { - this.duration = duration; - this.timeUnit = timeUnit; - customEvent = event; - eventInvocationCount++; - if (raiseErrorOnAllInvocations) { - throw new IllegalStateException("Deliberate exception."); - } - } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { - customEvent = event; - recievedError = throwable; - eventInvocationCount++; - if (raiseErrorOnAllInvocations) { - throw new IllegalStateException("Deliberate exception."); - } - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { - this.duration = duration; - this.timeUnit = timeUnit; - recievedError = throwable; - customEvent = event; - eventInvocationCount++; - if (raiseErrorOnAllInvocations) { - throw new IllegalStateException("Deliberate exception."); - } - } - - public int getOnCompletedCount() { - return onCompletedCount; - } - - public int getEventInvocationCount() { - return eventInvocationCount; - } - - public long getDuration() { - return duration; - } - - public TimeUnit getTimeUnit() { - return timeUnit; - } - - public Throwable getRecievedError() { - return recievedError; - } - - public String getArg() { - return arg; - } - - public Object getCustomEvent() { - return customEvent; - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockEventPublisher.java b/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockEventPublisher.java deleted file mode 100644 index 908e804..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockEventPublisher.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.test.util; - -import io.reactivex.netty.events.EventListener; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.events.EventSource; -import rx.Subscription; -import rx.subscriptions.Subscriptions; - -public class MockEventPublisher implements EventPublisher, EventSource { - - private static final MockEventPublisher DISABLED_EVENT_PUBLISHER = new MockEventPublisher(true); - private static final MockEventPublisher ENABLED_EVENT_PUBLISHER = new MockEventPublisher(false); - - private final boolean disable; - - private MockEventPublisher(boolean disable) { - this.disable = disable; - } - - public static MockEventPublisher disabled() { - @SuppressWarnings("unchecked") - MockEventPublisher t = (MockEventPublisher) DISABLED_EVENT_PUBLISHER; - return t; - } - - public static MockEventPublisher enabled() { - @SuppressWarnings("unchecked") - MockEventPublisher t = (MockEventPublisher) ENABLED_EVENT_PUBLISHER; - return t; - } - - @Override - public boolean publishingEnabled() { - return !disable; - } - - @Override - public Subscription subscribe(T listener) { - return Subscriptions.empty(); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockPoolLimitDeterminationStrategy.java b/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockPoolLimitDeterminationStrategy.java deleted file mode 100644 index 6570c93..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockPoolLimitDeterminationStrategy.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.test.util; - -import io.reactivex.netty.client.pool.MaxConnectionsBasedStrategy; -import io.reactivex.netty.client.pool.PoolLimitDeterminationStrategy; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -public class MockPoolLimitDeterminationStrategy implements PoolLimitDeterminationStrategy { - - private final MaxConnectionsBasedStrategy delegate; - private final AtomicInteger acquireCount = new AtomicInteger(); - private final AtomicInteger releaseCount = new AtomicInteger(); - - public MockPoolLimitDeterminationStrategy(int maxPermits) { - delegate = new MaxConnectionsBasedStrategy(maxPermits); - } - - @Override - public boolean acquireCreationPermit(long acquireStartTime, TimeUnit timeUnit) { - acquireCount.incrementAndGet(); - return delegate.acquireCreationPermit(acquireStartTime, timeUnit); - } - - @Override - public int getAvailablePermits() { - return delegate.getAvailablePermits(); - } - - @Override - public void releasePermit() { - releaseCount.incrementAndGet(); - delegate.releasePermit(); - } - - public int getAcquireCount() { - return acquireCount.get(); - } - - public int getReleaseCount() { - return releaseCount.get(); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockProducer.java b/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockProducer.java deleted file mode 100644 index 937289b..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockProducer.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.test.util; - -import rx.Producer; - -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; - -public class MockProducer implements Producer { - - private final AtomicLong requested = new AtomicLong(); - private final AtomicInteger negativeRequestCount = new AtomicInteger(); - private final AtomicInteger maxValueRequestedCount = new AtomicInteger(); - - @Override - public void request(long n) { - if (Long.MAX_VALUE == n) { - requested.set(Long.MAX_VALUE); - maxValueRequestedCount.incrementAndGet(); - } else if (n <= 0) { - negativeRequestCount.incrementAndGet(); - } - requested.addAndGet(n); - } - - public long getRequested() { - return requested.get(); - } - - public void assertIllegalRequest() { - final int negReqCnt = negativeRequestCount.get(); - if (negReqCnt != 0) { - throw new AssertionError("Negative items requested " + negReqCnt + " times."); - } - } - - public void assertBackpressureRequested() { - final int maxValReqCnt = maxValueRequestedCount.get(); - if (maxValReqCnt != 0) { - throw new AssertionError("Backpressure disabled " + maxValReqCnt + " times."); - } - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockTcpServerEventListener.java b/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockTcpServerEventListener.java deleted file mode 100644 index fce92ea..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/MockTcpServerEventListener.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.test.util; - -import io.reactivex.netty.protocol.tcp.server.events.TcpServerEventListener; -import io.reactivex.netty.test.util.MockConnectionEventListener; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class MockTcpServerEventListener extends TcpServerEventListener { - - public enum ServerEvent { - NewClient, HandlingStart, HandlingSuccess, HandlingFailed - } - - private final List methodsCalled = new ArrayList<>(); - private long duration; - private TimeUnit timeUnit; - private Throwable recievedError; - - private final MockConnectionEventListener connDelegate; - - public MockTcpServerEventListener() { - connDelegate = new MockConnectionEventListener(); - } - - @Override - public void onNewClientConnected() { - methodsCalled.add(ServerEvent.NewClient); - } - - @Override - public void onConnectionHandlingStart(long duration, TimeUnit timeUnit) { - this.duration = duration; - this.timeUnit = timeUnit; - methodsCalled.add(ServerEvent.HandlingStart); - } - - @Override - public void onConnectionHandlingSuccess(long duration, TimeUnit timeUnit) { - this.duration = duration; - this.timeUnit = timeUnit; - methodsCalled.add(ServerEvent.HandlingSuccess); - } - - @Override - public void onConnectionHandlingFailed(long duration, TimeUnit timeUnit, Throwable recievedError) { - this.duration = duration; - this.timeUnit = timeUnit; - this.recievedError = recievedError; - methodsCalled.add(ServerEvent.HandlingFailed); - } - - public long getDuration() { - return duration; - } - - public TimeUnit getTimeUnit() { - return timeUnit; - } - - public Throwable getRecievedError() { - return recievedError; - } - - @Override - public void onCompleted() { - connDelegate.onCompleted(); - } - - @Override - public void onConnectionCloseFailed(long duration, TimeUnit timeUnit, - Throwable recievedError) { - connDelegate.onConnectionCloseFailed(duration, timeUnit, recievedError); - } - - @Override - public void onConnectionCloseSuccess(long duration, TimeUnit timeUnit) { - connDelegate.onConnectionCloseSuccess(duration, timeUnit); - } - - @Override - public void onConnectionCloseStart() { - connDelegate.onConnectionCloseStart(); - } - - @Override - public void onWriteFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - connDelegate.onWriteFailed(duration, timeUnit, throwable); - } - - @Override - public void onWriteSuccess(long duration, TimeUnit timeUnit) { - connDelegate.onWriteSuccess(duration, timeUnit); - } - - @Override - public void onWriteStart() { - connDelegate.onWriteStart(); - } - - @Override - public void onFlushComplete(long duration, TimeUnit timeUnit) { - connDelegate.onFlushComplete(duration, timeUnit); - } - - @Override - public void onFlushStart() { - connDelegate.onFlushStart(); - } - - @Override - public void onByteRead(long bytesRead) { - connDelegate.onByteRead(bytesRead); - } - - @Override - public void onByteWritten(long bytesWritten) { - connDelegate.onByteWritten(bytesWritten); - } - - @Override - public void onCustomEvent(Object event) { - connDelegate.onCustomEvent(event); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit) { - connDelegate.onCustomEvent(event, duration, timeUnit); - } - - @Override - public void onCustomEvent(Object event, long duration, TimeUnit timeUnit, Throwable throwable) { - connDelegate.onCustomEvent(event, duration, timeUnit, throwable); - } - - @Override - public void onCustomEvent(Object event, Throwable throwable) { - connDelegate.onCustomEvent(event, throwable); - } - - public void assertMethodsCalled(ServerEvent... events) { - assertThat("Unexpected methods called count.", methodsCalled, hasSize(events.length)); - assertThat("Unexpected methods called.", methodsCalled, contains(events)); - } - - public MockConnectionEventListener getConnDelegate() { - return connDelegate; - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/TcpConnectionRequestMock.java b/netty-http-rx/src/test/java/io/reactivex/netty/test/util/TcpConnectionRequestMock.java deleted file mode 100644 index a2c21b1..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/TcpConnectionRequestMock.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.test.util; - -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.client.ConnectionRequest; -import rx.Observable; -import rx.Subscriber; - -public class TcpConnectionRequestMock extends ConnectionRequest { - - public TcpConnectionRequestMock(final Observable> connections) { - super(new OnSubscribe>() { - @Override - public void call(Subscriber> subscriber) { - connections.unsafeSubscribe(subscriber); - } - }); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/TrackableMetricEventsListener.java b/netty-http-rx/src/test/java/io/reactivex/netty/test/util/TrackableMetricEventsListener.java deleted file mode 100644 index 26a60a9..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/TrackableMetricEventsListener.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.test.util; - -import io.reactivex.netty.client.events.ClientEventListener; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -public class TrackableMetricEventsListener extends ClientEventListener { - - private final AtomicLong creationCount = new AtomicLong(); - private final AtomicLong failedCount = new AtomicLong(); - private final AtomicLong reuseCount = new AtomicLong(); - private final AtomicLong evictionCount = new AtomicLong(); - private final AtomicLong acquireAttemptedCount = new AtomicLong(); - private final AtomicLong acquireSucceededCount = new AtomicLong(); - private final AtomicLong acquireFailedCount = new AtomicLong(); - private final AtomicLong releaseAttemptedCount = new AtomicLong(); - private final AtomicLong releaseSucceededCount = new AtomicLong(); - private final AtomicLong releaseFailedCount = new AtomicLong(); - - @Override - public void onConnectSuccess(long duration, TimeUnit timeUnit) { - creationCount.incrementAndGet(); - } - - @Override - public void onConnectFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - failedCount.incrementAndGet(); - } - - @Override - public void onPoolReleaseStart() { - releaseAttemptedCount.incrementAndGet(); - } - - @Override - public void onPoolReleaseSuccess(long duration, TimeUnit timeUnit) { - releaseSucceededCount.incrementAndGet(); - } - - @Override - public void onPoolReleaseFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - releaseFailedCount.incrementAndGet(); - } - - @Override - public void onPooledConnectionEviction() { - evictionCount.incrementAndGet(); - } - - @Override - public void onPooledConnectionReuse() { - reuseCount.incrementAndGet(); - } - - @Override - public void onPoolAcquireStart() { - acquireAttemptedCount.incrementAndGet(); - } - - @Override - public void onPoolAcquireSuccess(long duration, TimeUnit timeUnit) { - acquireSucceededCount.incrementAndGet(); - } - - @Override - public void onPoolAcquireFailed(long duration, TimeUnit timeUnit, Throwable throwable) { - acquireFailedCount.incrementAndGet(); - } - - public long getAcquireAttemptedCount() { - return acquireAttemptedCount.longValue(); - } - - public long getAcquireFailedCount() { - return acquireFailedCount.longValue(); - } - - public long getAcquireSucceededCount() { - return acquireSucceededCount.longValue(); - } - - public long getCreationCount() { - return creationCount.longValue(); - } - - public long getEvictionCount() { - return evictionCount.longValue(); - } - - public long getFailedCount() { - return failedCount.longValue(); - } - - public long getReleaseAttemptedCount() { - return releaseAttemptedCount.longValue(); - } - - public long getReleaseFailedCount() { - return releaseFailedCount.longValue(); - } - - public long getReleaseSucceededCount() { - return releaseSucceededCount.longValue(); - } - - public long getReuseCount() { - return reuseCount.longValue(); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/embedded/EmbeddedChannelPipelineDelegate.java b/netty-http-rx/src/test/java/io/reactivex/netty/test/util/embedded/EmbeddedChannelPipelineDelegate.java deleted file mode 100644 index 27f6cd3..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/embedded/EmbeddedChannelPipelineDelegate.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.test.util.embedded; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.ChannelProgressivePromise; -import io.netty.channel.ChannelPromise; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.concurrent.EventExecutorGroup; - -import java.net.SocketAddress; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.atomic.AtomicLong; - -class EmbeddedChannelPipelineDelegate implements ChannelPipeline { - - private final ChannelPipeline pipeline; - private final String lastHandlerName; - private final AtomicLong uniqueNameCounter = new AtomicLong(); - - public EmbeddedChannelPipelineDelegate(EmbeddedChannel newChannel) { - pipeline = newChannel.pipeline(); - final ChannelHandler lastHandler = pipeline.last(); - if (null != lastHandler) { - List names = pipeline.names(); - for (String name : names) { - if (pipeline.get(name) == lastHandler) { - lastHandlerName = name; - return; - } - } - lastHandlerName = null; - } else { - lastHandlerName = null; - } - } - - @Override - public ChannelPipeline addFirst(String name, ChannelHandler handler) { - pipeline.addFirst(name, handler); - return this; - } - - @Override - public ChannelPipeline addFirst(EventExecutorGroup group, - String name, ChannelHandler handler) { - pipeline.addFirst(group, name, handler); - return this; - } - - @Override - public ChannelPipeline addLast(String name, ChannelHandler handler) { - if (null != lastHandlerName) { - pipeline.addBefore(lastHandlerName, name, handler); - } else { - pipeline.addLast(name, handler); - } - return this; - } - - @Override - public ChannelPipeline addLast(EventExecutorGroup group, - String name, ChannelHandler handler) { - if (null != lastHandlerName) { - pipeline.addBefore(group, lastHandlerName, name, handler); - } else { - pipeline.addLast(group, name, handler); - } - return this; - } - - @Override - public ChannelPipeline addBefore(String baseName, String name, - ChannelHandler handler) { - pipeline.addBefore(baseName, name, handler); - return this; - } - - @Override - public ChannelPipeline addBefore(EventExecutorGroup group, - String baseName, String name, - ChannelHandler handler) { - pipeline.addBefore(group, baseName, name, handler); - return this; - } - - @Override - public ChannelPipeline addAfter(String baseName, String name, - ChannelHandler handler) { - pipeline.addAfter(baseName, name, handler); - return this; - } - - @Override - public ChannelPipeline addAfter(EventExecutorGroup group, - String baseName, String name, - ChannelHandler handler) { - pipeline.addAfter(group, baseName, name, handler); - return this; - } - - @Override - public ChannelPipeline addFirst(ChannelHandler... handlers) { - pipeline.addFirst(handlers); - return this; - } - - @Override - public ChannelPipeline addFirst(EventExecutorGroup group, - ChannelHandler... handlers) { - pipeline.addFirst(group, handlers); - return this; - } - - @Override - public ChannelPipeline addLast(ChannelHandler... handlers) { - if (null != lastHandlerName) { - for (ChannelHandler handler : handlers) { - pipeline.addBefore(lastHandlerName, generateUniqueName(), handler); - } - } else { - pipeline.addLast(handlers); - } - return this; - } - - @Override - public ChannelPipeline addLast(EventExecutorGroup group, - ChannelHandler... handlers) { - if (null != lastHandlerName) { - for (ChannelHandler handler : handlers) { - pipeline.addBefore(group, lastHandlerName, generateUniqueName(), handler); - } - } else { - pipeline.addLast(group, handlers); - } - return this; - } - - @Override - public ChannelPipeline remove(ChannelHandler handler) { - return pipeline.remove(handler); - } - - @Override - public ChannelHandler remove(String name) { - return pipeline.remove(name); - } - - @Override - public T remove(Class handlerType) { - return pipeline.remove(handlerType); - } - - @Override - public ChannelHandler removeFirst() { - return pipeline.removeFirst(); - } - - @Override - public ChannelHandler removeLast() { - return pipeline.removeLast(); - } - - @Override - public ChannelPipeline replace(ChannelHandler oldHandler, - String newName, ChannelHandler newHandler) { - return pipeline.replace(oldHandler, newName, newHandler); - } - - @Override - public ChannelHandler replace(String oldName, String newName, - ChannelHandler newHandler) { - return pipeline.replace(oldName, newName, newHandler); - } - - @Override - public T replace(Class oldHandlerType, String newName, - ChannelHandler newHandler) { - return pipeline.replace(oldHandlerType, newName, newHandler); - } - - private String generateUniqueName() { - return "ClientEmbeddedConnectionFactoryGeneratedName-" + uniqueNameCounter.incrementAndGet(); - } - - @Override - public ChannelHandler first() { - return pipeline.first(); - } - - @Override - public ChannelHandlerContext firstContext() { - return pipeline.firstContext(); - } - - @Override - public ChannelHandler last() { - return pipeline.last(); - } - - @Override - public ChannelHandlerContext lastContext() { - return pipeline.lastContext(); - } - - @Override - public ChannelHandler get(String name) { - return pipeline.get(name); - } - - @Override - public T get(Class handlerType) { - return pipeline.get(handlerType); - } - - @Override - public ChannelHandlerContext context(ChannelHandler handler) { - return pipeline.context(handler); - } - - @Override - public ChannelHandlerContext context(String name) { - return pipeline.context(name); - } - - @Override - public ChannelHandlerContext context( - Class handlerType) { - return pipeline.context(handlerType); - } - - @Override - public Channel channel() { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public List names() { - return pipeline.names(); - } - - @Override - public Map toMap() { - return pipeline.toMap(); - } - - @Override - public ChannelPipeline fireChannelRegistered() { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelPipeline fireChannelUnregistered() { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelPipeline fireChannelActive() { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelPipeline fireChannelInactive() { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelPipeline fireExceptionCaught(Throwable cause) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelPipeline fireUserEventTriggered(Object event) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelPipeline fireChannelRead(Object msg) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelPipeline fireChannelReadComplete() { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelPipeline fireChannelWritabilityChanged() { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture bind(SocketAddress localAddress) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture connect(SocketAddress remoteAddress) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture connect(SocketAddress remoteAddress, - SocketAddress localAddress) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture disconnect() { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture close() { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture deregister() { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture bind(SocketAddress localAddress, - ChannelPromise promise) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture connect(SocketAddress remoteAddress, - ChannelPromise promise) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture connect(SocketAddress remoteAddress, - SocketAddress localAddress, - ChannelPromise promise) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture disconnect(ChannelPromise promise) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture close(ChannelPromise promise) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture deregister(ChannelPromise promise) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelPipeline read() { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture write(Object msg) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture write(Object msg, ChannelPromise promise) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelPipeline flush() { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture writeAndFlush(Object msg) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelPromise newPromise() { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelProgressivePromise newProgressivePromise() { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture newSucceededFuture() { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelFuture newFailedFuture(Throwable cause) { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public ChannelPromise voidPromise() { - throw new UnsupportedOperationException("Only pipeline modification operations are allowed"); - } - - @Override - public Iterator> iterator() { - return pipeline.iterator(); - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/embedded/EmbeddedChannelProvider.java b/netty-http-rx/src/test/java/io/reactivex/netty/test/util/embedded/EmbeddedChannelProvider.java deleted file mode 100644 index d9d7287..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/embedded/EmbeddedChannelProvider.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.test.util.embedded; - -import io.netty.channel.Channel; -import io.netty.channel.embedded.EmbeddedChannel; -import io.reactivex.netty.client.ChannelProvider; -import io.reactivex.netty.client.ChannelProviderFactory; -import io.reactivex.netty.client.Host; -import io.reactivex.netty.client.events.ClientEventListener; -import io.reactivex.netty.events.EventPublisher; -import io.reactivex.netty.events.EventSource; -import io.reactivex.netty.test.util.InboundRequestFeeder; -import rx.Observable; -import rx.Observable.Operator; -import rx.Subscriber; -import rx.functions.Func0; - -import java.io.IOException; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -public class EmbeddedChannelProvider implements ChannelProvider { - - private final List createdChannels = new CopyOnWriteArrayList<>(); - private final boolean failConnect; - - public EmbeddedChannelProvider() { - this(false); - } - - public EmbeddedChannelProvider(boolean failConnect) { - this.failConnect = failConnect; - } - - @Override - public Observable newChannel(Observable input) { - if (failConnect) { - return Observable.error(new IOException("Deliberate connection failure")); - } - - return Observable.defer(new Func0>() { - @Override - public Observable call() { - InboundRequestFeeder feeder = new InboundRequestFeeder(); - EmbeddedChannel embeddedChannel = new EmbeddedChannel(feeder); - EmbeddedChannelWithFeeder ecwf = new EmbeddedChannelWithFeeder(embeddedChannel, feeder); - createdChannels.add(ecwf); - return Observable.just(embeddedChannel); - } - }).lift(new Operator() { - @Override - public Subscriber call(final Subscriber subscriber) { - return new Subscriber(subscriber) { - @Override - public void onCompleted() { - subscriber.onCompleted(); - } - - @Override - public void onError(Throwable e) { - subscriber.onError(e); - } - - @Override - public void onNext(Channel channel) { - subscriber.onNext(channel); - channel.pipeline().fireChannelActive(); - } - }; - } - }); - } - - public List getCreatedChannels() { - return createdChannels; - } - - public ChannelProviderFactory asFactory() { - return new ChannelProviderFactory() { - @Override - public ChannelProvider newProvider(Host host, EventSource eventSource, - EventPublisher publisher, ClientEventListener clientPublisher) { - return EmbeddedChannelProvider.this; - } - }; - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/embedded/EmbeddedChannelWithFeeder.java b/netty-http-rx/src/test/java/io/reactivex/netty/test/util/embedded/EmbeddedChannelWithFeeder.java deleted file mode 100644 index 9aafad9..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/embedded/EmbeddedChannelWithFeeder.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.test.util.embedded; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.reactivex.netty.client.events.ClientEventListener; -import io.reactivex.netty.events.EventSource; -import io.reactivex.netty.test.util.MockEventPublisher; -import io.reactivex.netty.test.util.InboundRequestFeeder; - -public class EmbeddedChannelWithFeeder { - - private final EmbeddedChannel channel; - private final InboundRequestFeeder feeder; - private final EventSource tcpEventSource; - - public EmbeddedChannelWithFeeder(EmbeddedChannel channel, InboundRequestFeeder feeder) { - this(channel, feeder, MockEventPublisher.disabled()); - } - - public EmbeddedChannelWithFeeder(EmbeddedChannel channel, InboundRequestFeeder feeder, - EventSource tcpEventSource) { - this.channel = channel; - this.feeder = feeder; - this.tcpEventSource = tcpEventSource; - } - - public EmbeddedChannel getChannel() { - return channel; - } - - public InboundRequestFeeder getFeeder() { - return feeder; - } - - public EventSource getTcpEventSource() { - return tcpEventSource; - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/embedded/EmbeddedConnectionProvider.java b/netty-http-rx/src/test/java/io/reactivex/netty/test/util/embedded/EmbeddedConnectionProvider.java deleted file mode 100644 index 62407e2..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/test/util/embedded/EmbeddedConnectionProvider.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.test.util.embedded; - -import io.netty.channel.Channel; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.channel.ConnectionImpl; -import io.reactivex.netty.client.ConnectionProvider; -import io.reactivex.netty.client.ConnectionProviderFactory; -import io.reactivex.netty.client.HostConnector; -import rx.Observable; -import rx.functions.Func1; - -public class EmbeddedConnectionProvider implements ConnectionProvider { - - private final EmbeddedChannelProvider channelProvider; - - public EmbeddedConnectionProvider() { - this(new EmbeddedChannelProvider()); - } - - public EmbeddedConnectionProvider(EmbeddedChannelProvider channelProvider) { - this.channelProvider = channelProvider; - } - - @Override - public Observable> newConnectionRequest() { - return channelProvider.newChannel(Observable.empty()) - .map(new Func1>() { - @Override - public Connection call(Channel channel) { - return ConnectionImpl.fromChannel(channel); - } - }); - } - - public EmbeddedChannelProvider getChannelProvider() { - return channelProvider; - } - - public ConnectionProviderFactory asFactory() { - return new ConnectionProviderFactory() { - @Override - public ConnectionProvider newProvider(Observable> hosts) { - return EmbeddedConnectionProvider.this; - } - }; - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/threads/PreferCurrentEventLoopGroupTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/threads/PreferCurrentEventLoopGroupTest.java deleted file mode 100644 index 87816e3..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/threads/PreferCurrentEventLoopGroupTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.threads; - -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import org.junit.Test; - -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; - -import static org.junit.Assert.*; - -public class PreferCurrentEventLoopGroupTest { - - @Test(timeout = 60000) - public void testNextInEventloop() throws Exception { - final PreferCurrentEventLoopGroup group = new PreferCurrentEventLoopGroup(new NioEventLoopGroup(4)); - for (EventExecutor child : group) { - Future future = child.submit(new Callable() { - @Override - public Boolean call() throws Exception { - return group.next().inEventLoop(); - } - }); - assertTrue("Current eventloop was not preferred.", future.get(1, TimeUnit.MINUTES)); - } - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/threads/RxJavaEventloopSchedulerTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/threads/RxJavaEventloopSchedulerTest.java deleted file mode 100644 index 50dc6ac..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/threads/RxJavaEventloopSchedulerTest.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.threads; - -import io.netty.channel.nio.NioEventLoopGroup; -import io.reactivex.netty.threads.RxJavaEventloopScheduler.EventloopWorker; -import org.junit.Test; -import rx.Observable; -import rx.Subscription; -import rx.functions.Action0; -import rx.observers.TestSubscriber; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class RxJavaEventloopSchedulerTest { - - @Test(timeout = 60000) - public void testScheduleNow() throws Exception { - RxJavaEventloopScheduler scheduler = new RxJavaEventloopScheduler(new NioEventLoopGroup()); - TestSubscriber testSubscriber = new TestSubscriber<>(); - - Observable.range(1, 1) - .observeOn(scheduler) - .subscribe(testSubscriber); - - testSubscriber.awaitTerminalEvent(); - testSubscriber.assertNoErrors(); - - testSubscriber.assertValue(1); - } - - @Test(timeout = 60000) - public void testScheduleDelay() throws Exception { - RxJavaEventloopScheduler scheduler = new RxJavaEventloopScheduler(new NioEventLoopGroup()); - TestSubscriber testSubscriber = new TestSubscriber<>(); - - Observable.timer(1, TimeUnit.MILLISECONDS, scheduler) - .subscribe(testSubscriber); - - testSubscriber.awaitTerminalEvent(); - testSubscriber.assertNoErrors(); - - testSubscriber.assertValue(0L); - } - - @Test(timeout = 60000) - public void testRemoveNonDelayedTasks() throws Exception { - RxJavaEventloopScheduler scheduler = new RxJavaEventloopScheduler(new NioEventLoopGroup()); - - final EventloopWorker worker = (EventloopWorker) scheduler.createWorker(); - final EventloopWorker worker2 = (EventloopWorker) scheduler.createWorker(); - - assertThat("New worker already has subscriptions.", worker.hasScheduledSubscriptions(), is(false)); - - final AtomicBoolean isScheduledBeforeExecute = new AtomicBoolean(); - final CountDownLatch executed = new CountDownLatch(1); - - Subscription subscription = worker.schedule(new Action0() { - @Override - public void call() { - isScheduledBeforeExecute.set(worker.hasScheduledSubscriptions()); - worker2.schedule(new Action0() { - @Override - public void call() { - executed.countDown(); - } - }); - } - }); - - executed.await(); - - assertThat("No scheduled subscriptions on executing the action.", isScheduledBeforeExecute.get(), is(true)); - assertThat("Action not unsubscribed.", subscription.isUnsubscribed(), is(true)); - - assertThat("Subscription not removed post execution.", worker.hasScheduledSubscriptions(), is(false)); - } - - @Test(timeout = 60000) - public void testRemoveDelayedTasks() throws Exception { - RxJavaEventloopScheduler scheduler = new RxJavaEventloopScheduler(new NioEventLoopGroup()); - - final EventloopWorker worker = (EventloopWorker) scheduler.createWorker(); - final EventloopWorker worker2 = (EventloopWorker) scheduler.createWorker(); - - assertThat("New worker already has subscriptions.", worker.hasDelayScheduledSubscriptions(), is(false)); - - final AtomicBoolean isScheduledBeforeExecute = new AtomicBoolean(); - final CountDownLatch executed = new CountDownLatch(1); - - Subscription subscription = worker.schedule(new Action0() { - @Override - public void call() { - isScheduledBeforeExecute.set(worker.hasDelayScheduledSubscriptions()); - worker2.schedule(new Action0() { - @Override - public void call() { - executed.countDown(); - } - }); - } - }, 1, TimeUnit.MILLISECONDS); - - executed.await(); - - assertThat("No scheduled subscriptions on executing the action.", isScheduledBeforeExecute.get(), is(true)); - assertThat("Action not unsubscribed.", subscription.isUnsubscribed(), is(true)); - - assertThat("Subscription not removed post execution.", worker.hasDelayScheduledSubscriptions(), is(false)); - } - - @Test(timeout = 60000) - public void testUnsubscribeDelayedTasks() throws Exception { - RxJavaEventloopScheduler scheduler = new RxJavaEventloopScheduler(new NioEventLoopGroup()); - - final EventloopWorker worker = (EventloopWorker) scheduler.createWorker(); - - assertThat("New worker already has subscriptions.", worker.hasDelayScheduledSubscriptions(), is(false)); - - Subscription subscription = worker.schedule(new Action0() { - @Override - public void call() { - } - }, 100, TimeUnit.DAYS); - - assertThat("No subscriptions post schedule.", worker.hasDelayScheduledSubscriptions(), is(true)); - - subscription.unsubscribe(); - - assertThat("Subscription not removed post cancellation.", worker.hasDelayScheduledSubscriptions(), is(false)); - } -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/util/CollectBytesTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/util/CollectBytesTest.java deleted file mode 100644 index df3ddad..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/util/CollectBytesTest.java +++ /dev/null @@ -1,211 +0,0 @@ -package io.reactivex.netty.util; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.ReferenceCountUtil; -import io.reactivex.netty.util.CollectBytes.TooMuchDataException; -import org.junit.Assert; -import org.junit.Test; -import rx.Observable; -import rx.functions.Action1; -import rx.functions.Func1; -import rx.observers.TestSubscriber; - -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class CollectBytesTest { - @Test - public void testCollectOverEmptyObservable() throws Exception { - TestSubscriber t = new TestSubscriber<>(); - Observable.empty() - .compose(CollectBytes.all()) - .subscribe(t); - - t.assertNoErrors(); - t.assertCompleted(); - t.assertValue(Unpooled.buffer()); - } - - @Test - public void testCollectSingleEvent() throws Exception { - TestSubscriber t = new TestSubscriber<>(); - Observable.just(getByteBuf("test")) - .compose(CollectBytes.all()) - .subscribe(t); - - t.assertNoErrors(); - t.assertCompleted(); - t.assertValues(getByteBuf("test")); - } - - @Test - public void testCollectManyEvents() throws Exception { - TestSubscriber t = new TestSubscriber<>(); - Observable.just( - getByteBuf("t"), - getByteBuf("e"), - getByteBuf("s"), - getByteBuf("t") - ) - .compose(CollectBytes.all()) - .subscribe(t); - - t.assertNoErrors(); - t.assertCompleted(); - t.assertValues(getByteBuf("test")); - } - - @Test - public void testWithLimitEqualToBytes() throws Exception { - TestSubscriber t = new TestSubscriber<>(); - Observable.just( - getByteBuf("t"), - getByteBuf("e"), - getByteBuf("s"), - getByteBuf("t") - ) - .compose(CollectBytes.upTo(4)) - .subscribe(t); - - t.assertNoErrors(); - t.assertCompleted(); - t.assertValues(getByteBuf("test")); - } - - @Test - public void testWithLimitGreaterThanBytes() throws Exception { - TestSubscriber t = new TestSubscriber<>(); - Observable.just( - getByteBuf("t"), - getByteBuf("e"), - getByteBuf("s"), - getByteBuf("t") - ) - .compose(CollectBytes.upTo(5)) - .subscribe(t); - - t.assertNoErrors(); - t.assertCompleted(); - t.assertValues(getByteBuf("test")); - } - - @Test - public void testCollectWithLimitSmallerThanBytes() throws Exception { - TestSubscriber t = new TestSubscriber<>(); - Observable.just( - getByteBuf("t"), - getByteBuf("e"), - getByteBuf("s"), - getByteBuf("t") - ) - .compose(CollectBytes.upTo(2)) - .subscribe(t); - - t.assertError(TooMuchDataException.class); - t.assertNotCompleted(); - t.assertNoValues(); - } - - @Test - public void testReturnSingleEventWithMoreBytesThanMax() throws Exception { - TestSubscriber t = new TestSubscriber<>(); - toByteBufObservable("test") - .compose(CollectBytes.upTo(0)) - .subscribe(t); - - t.assertError(TooMuchDataException.class); - t.assertNotCompleted(); - t.assertNoValues(); - } - - @Test - public void testReturnMultipleEvents() throws Exception { - TestSubscriber t = new TestSubscriber<>(); - toByteBufObservable("1", "2") - .compose(CollectBytes.upTo(5)) - .subscribe(t); - - t.assertNoErrors(); - t.assertCompleted(); - t.assertValues(getByteBufs("12")); - } - - @Test - public void testReturnEventsOnLimitBoundary() throws Exception { - TestSubscriber t = new TestSubscriber<>(); - toByteBufObservable("12", "34", "56") - .compose(CollectBytes.upTo(4)) - .subscribe(t); - - t.assertError(TooMuchDataException.class); - t.assertNotCompleted(); - t.assertNoValues(); - } - - @Test - public void testReturnMultipleEventsEndingWhenOverMaxBytes() throws Exception { - TestSubscriber t = new TestSubscriber<>(); - toByteBufObservable("first", "second", "third") - .compose(CollectBytes.upTo(7)) - .subscribe(t); - - t.assertError(TooMuchDataException.class); - t.assertNotCompleted(); - t.assertNoValues(); - } - - @Test - public void testUnsubscribeFromUpstream() throws Exception { - final List emittedBufs = new ArrayList<>(); - - toByteBufObservable("first", "second", "third") - .doOnNext(new Action1() { - @Override - public void call(ByteBuf byteBuf) { - emittedBufs.add(byteBuf.toString(Charset.defaultCharset())); - } - }) - .compose(CollectBytes.upTo(7)) - .subscribe(new TestSubscriber<>()); - - Assert.assertEquals(Arrays.asList("first", "second"), emittedBufs); - } - - @Test(expected = IllegalArgumentException.class) - public void testExceptionOnNegativeMaxBytes() throws Exception { - CollectBytes.upTo(-1); - } - - private Observable toByteBufObservable(String... values) { - return Observable.from(values) - .map(new Func1() { - @Override - public ByteBuf call(String s) { - return getByteBuf(s); - } - }); - } - - private ByteBuf getBytes(int length, int value) { - ByteBuf buffer = Unpooled.buffer(length, length); - for (int i = 0; i < length; ++i) { - buffer.writeByte(value); - } - return buffer; - } - - private ByteBuf getByteBuf(String s) { - return ReferenceCountUtil.releaseLater(Unpooled.copiedBuffer(s, Charset.defaultCharset())); - } - - private ByteBuf[] getByteBufs(String... s) { - ByteBuf[] bufs = new ByteBuf[s.length]; - for (int i = 0; i < s.length; ++i) { - bufs[i] = getByteBuf(s[i]); - } - return bufs; - } -} diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/util/LineReaderTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/util/LineReaderTest.java deleted file mode 100644 index d2ef3d3..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/util/LineReaderTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * 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 io.reactivex.netty.util; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import java.util.ArrayList; -import java.util.List; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class LineReaderTest { - - @Rule - public final ReaderRule readerRule = new ReaderRule(); - - @Test(timeout = 60000) - public void testSingleLine() throws Exception { - String msg = "Hello"; - ByteBuf data = Unpooled.buffer().writeBytes((msg + "\n").getBytes()); - List out = new ArrayList<>(); - readerRule.lineReader.decode(data, out, UnpooledByteBufAllocator.DEFAULT); - - assertThat("Unexpected output size.", out, hasSize(1)); - assertThat("Unexpected output message.", out, contains((Object) msg)); - assertThat("Input buffer not consumed.", data.isReadable(), is(false)); - } - - @Test(timeout = 60000) - public void testEmptyInputBuffer() throws Exception { - ByteBuf data = Unpooled.buffer(); - List out = new ArrayList<>(); - readerRule.lineReader.decode(data, out, UnpooledByteBufAllocator.DEFAULT); - - assertThat("Unexpected output size.", out, is(empty())); - assertThat("Input buffer not consumed.", data.isReadable(), is(false)); - } - - @Test(timeout = 60000) - public void testEmptyLine() throws Exception { - ByteBuf data = Unpooled.buffer().writeByte('\n'); - List out = new ArrayList<>(); - readerRule.lineReader.decode(data, out, UnpooledByteBufAllocator.DEFAULT); - - assertThat("Unexpected output size.", out, hasSize(1)); - assertThat("Unexpected output message.", out, contains((Object)"")); - assertThat("Input buffer not consumed.", data.isReadable(), is(false)); - } - - @Test(timeout = 60000) - public void testSplitData() throws Exception { - String msg1 = "Hell"; - String msg2 = "o"; - ByteBuf data1 = Unpooled.buffer().writeBytes((msg1.getBytes())); - ByteBuf data2 = Unpooled.buffer().writeBytes((msg2 + "\n").getBytes()); - List out = new ArrayList<>(); - readerRule.lineReader.decode(data1, out, UnpooledByteBufAllocator.DEFAULT); - assertThat("Unexpected output size post first decode.", out, is(empty())); - assertThat("Input buffer not consumed.", data1.isReadable(), is(false)); - - readerRule.lineReader.decode(data2, out, UnpooledByteBufAllocator.DEFAULT); - assertThat("Unexpected output size post second decode.", out, hasSize(1)); - assertThat("Unexpected output message post second decode.", out, contains((Object) (msg1 + msg2))); - assertThat("Input buffer not consumed.", data2.isReadable(), is(false)); - } - - @Test(timeout = 60000) - public void testUnreadDataDispose() throws Exception { - String msg = "Hell"; - ByteBuf data1 = Unpooled.buffer().writeBytes((msg.getBytes())); - List out = new ArrayList<>(); - readerRule.lineReader.decode(data1, out, UnpooledByteBufAllocator.DEFAULT); - assertThat("Unexpected output size post first decode.", out, is(empty())); - assertThat("Input buffer not consumed.", data1.isReadable(), is(false)); - assertThat("Reader does not have incomplete buffer.", readerRule.lineReader.getIncompleteBuffer(), is(notNullValue())); - assertThat("Reader's incomplete buffer is not readable.", readerRule.lineReader.getIncompleteBuffer() - .isReadable(), - is(true)); - - readerRule.lineReader.dispose(); - - assertThat("Reader does not have incomplete buffer.", readerRule.lineReader.getIncompleteBuffer(), - is(notNullValue())); - assertThat("Reader did not release incomplete buffer.", readerRule.lineReader.getIncompleteBuffer().refCnt(), - is(0)); - } - - public static class ReaderRule extends ExternalResource { - - private LineReader lineReader; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - lineReader = new LineReader(); - base.evaluate(); - } - }; - } - } - -} \ No newline at end of file diff --git a/netty-http-rx/src/test/java/io/reactivex/netty/util/UnicastBufferingSubjectTest.java b/netty-http-rx/src/test/java/io/reactivex/netty/util/UnicastBufferingSubjectTest.java deleted file mode 100644 index a5ef79e..0000000 --- a/netty-http-rx/src/test/java/io/reactivex/netty/util/UnicastBufferingSubjectTest.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * 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 io.reactivex.netty.util; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import rx.Observable; -import rx.exceptions.MissingBackpressureException; -import rx.observers.TestSubscriber; - -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; - -public class UnicastBufferingSubjectTest { - - @Rule - public final ExpectedException expectedException = ExpectedException.none(); - - @Rule - public final SubjectRule rule = new SubjectRule(); - - @Test(timeout = 60000) - public void testSequentialSubscriptions() throws Exception { - String[] msgs = { "Hello1", "Hello2" }; - - rule.subject.onNext(msgs[0]); - - rule.subscribeAndAssertValues(rule.subject.take(1), msgs[0]); - - rule.subject.onNext(msgs[1]); - - rule.subscribeAndAssertValues(rule.subject.take(1), msgs[1]); - } - - @Test(timeout = 60000) - public void testConcurrentSubscriptions() throws Exception { - TestSubscriber sub1 = new TestSubscriber<>(); - rule.subject.subscribe(sub1); - - sub1.assertNoTerminalEvent(); - - TestSubscriber sub2 = new TestSubscriber<>(); - rule.subject.subscribe(sub2); - - sub2.assertError(IllegalStateException.class); - } - - @Test(timeout = 60000) - public void testBufferCompletion() throws Exception { - rule.subject.onCompleted(); - rule.subscribeAndAssertValues(rule.subject); - } - - @Test(timeout = 60000) - public void testBufferError() throws Exception { - rule.subject.onError(new IllegalStateException()); - - TestSubscriber sub = new TestSubscriber<>(); - rule.subject.subscribe(sub); - - sub.assertTerminalEvent(); - sub.assertError(IllegalStateException.class); - } - - @Test(timeout = 60000) - public void testUnsubscribeBeforeDemandComplete() throws Exception { - String[] msgs = { "Hello1", "Hello2" }; - - rule.subject.onNext(msgs[0]); - rule.subject.onNext(msgs[1]); - rule.subject.onCompleted(); - - rule.subscribeAndAssertValues(rule.subject.take(1), msgs[0]); - - TestSubscriber sub = new TestSubscriber<>(0); - rule.subject.subscribe(sub); - - sub.assertNoTerminalEvent(); - sub.assertNoValues(); - - sub.requestMore(1); - - sub.assertTerminalEvent(); - sub.assertNoErrors(); - sub.assertValue(msgs[1]); - } - - @Test(timeout = 60000) - public void testBufferOverflowWithOffer() throws Exception { - String[] msgs = { "Hello1", "Hello2" }; - UnicastBufferingSubject subject = UnicastBufferingSubject.create(1); - - subject.onNext(msgs[0]); - boolean offered = subject.offerNext(msgs[1]); - - assertThat("Offered passed when over capacity", offered, is(false)); - } - - @Test(timeout = 60000) - public void testBufferOverflowWithOnNext() throws Exception { - expectedException.expectCause(isA(MissingBackpressureException.class)); - - String[] msgs = { "Hello1", "Hello2" }; - UnicastBufferingSubject subject = UnicastBufferingSubject.create(1); - - subject.onNext(msgs[0]); - subject.onNext(msgs[1]); - } - - @Test(timeout = 60000) - public void testOverflowSubscribeAndThenAccept() throws Exception { - String[] msgs = { "Hello1", "Hello2" }; - UnicastBufferingSubject subject = UnicastBufferingSubject.create(1); - - subject.onNext(msgs[0]); - boolean offered = subject.offerNext(msgs[1]); - - assertThat("Offered passed when over capacity", offered, is(false)); - - TestSubscriber subscriber = new TestSubscriber<>(); - subject.subscribe(subscriber); - - subscriber.assertNoTerminalEvent(); - subscriber.assertValue(msgs[0]); - - subject.onNext(msgs[1]); - subject.onCompleted(); - - subscriber.assertTerminalEvent(); - subscriber.assertNoErrors(); - subscriber.assertValues(msgs); - } - - @Test(timeout = 60000) - public void testErrorPostSubscribe() throws Exception { - String[] msgs = { "Hello1", "Hello2" }; - - rule.subject.onNext(msgs[0]); - - TestSubscriber subscriber = new TestSubscriber<>(); - rule.subject.subscribe(subscriber); - - subscriber.assertNoTerminalEvent(); - subscriber.assertValue(msgs[0]); - - rule.subject.onError(new IllegalStateException()); - - subscriber.assertTerminalEvent(); - subscriber.assertError(IllegalStateException.class); - } - - private static class SubjectRule extends ExternalResource { - - private UnicastBufferingSubject subject; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - subject = UnicastBufferingSubject.create(Long.MAX_VALUE); - base.evaluate(); - } - }; - } - - public void subscribeAndAssertValues(Observable source, String... values) { - TestSubscriber sub1 = new TestSubscriber<>(); - source.subscribe(sub1); - sub1.assertTerminalEvent(); - sub1.assertNoErrors(); - sub1.assertValues(values); - } - } - -} \ No newline at end of file diff --git a/netty-http-rx/src/test/resources/log4j.properties b/netty-http-rx/src/test/resources/log4j.properties deleted file mode 100644 index 176c09e..0000000 --- a/netty-http-rx/src/test/resources/log4j.properties +++ /dev/null @@ -1,21 +0,0 @@ -# -# Copyright 2015 Netflix, Inc. -# -# 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. -# -# -log4j.rootLogger=INFO, stdout - -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{dd MMM yyyy HH:mm:ss,SSS} %5p [%t] (%F:%L) - %m%n \ No newline at end of file diff --git a/netty-http-server-reactive/NOTICE.txt b/netty-http-server-reactive/NOTICE.txt deleted file mode 100644 index a292203..0000000 --- a/netty-http-server-reactive/NOTICE.txt +++ /dev/null @@ -1,3 +0,0 @@ -This work is based on - -https://github.com/playframework/netty-reactive-streams/ diff --git a/netty-http-server-reactive/build.gradle b/netty-http-server-reactive/build.gradle deleted file mode 100644 index dce05f7..0000000 --- a/netty-http-server-reactive/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -dependencies { - api "org.reactivestreams:reactive-streams:${project.property('reactivestreams.version')}" - api project(':netty-http-server') - testImplementation("org.reactivestreams:reactive-streams-tck:${project.property('reactivestreams.version')}") { - exclude module: 'testng' - } -} \ No newline at end of file diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/CancelledSubscriber.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/CancelledSubscriber.java deleted file mode 100644 index 57cd7d7..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/CancelledSubscriber.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -/** - * A cancelled subscriber. - */ -public final class CancelledSubscriber implements Subscriber { - - @Override - public void onSubscribe(Subscription subscription) { - if (subscription == null) { - throw new NullPointerException("Null subscription"); - } else { - subscription.cancel(); - } - } - - @Override - public void onNext(T t) { - } - - @Override - public void onError(Throwable error) { - if (error == null) { - throw new NullPointerException("Null error published"); - } - } - - @Override - public void onComplete() { - } -} diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateHttpMessage.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateHttpMessage.java deleted file mode 100644 index 86bcde0..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateHttpMessage.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpVersion; - -class DelegateHttpMessage implements HttpMessage { - protected final HttpMessage message; - - public DelegateHttpMessage(HttpMessage message) { - this.message = message; - } - - @Override - @Deprecated - public HttpVersion getProtocolVersion() { - return message.protocolVersion(); - } - - @Override - public HttpVersion protocolVersion() { - return message.protocolVersion(); - } - - @Override - public HttpMessage setProtocolVersion(HttpVersion version) { - message.setProtocolVersion(version); - return this; - } - - @Override - public HttpHeaders headers() { - return message.headers(); - } - - @Override - @Deprecated - public DecoderResult getDecoderResult() { - return message.decoderResult(); - } - - @Override - public DecoderResult decoderResult() { - return message.decoderResult(); - } - - @Override - public void setDecoderResult(DecoderResult result) { - message.setDecoderResult(result); - } - - @Override - public String toString() { - return this.getClass().getName() + "(" + message.toString() + ")"; - } - -} diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateHttpRequest.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateHttpRequest.java deleted file mode 100644 index 97142fb..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateHttpRequest.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; - -class DelegateHttpRequest extends DelegateHttpMessage implements HttpRequest { - - protected final HttpRequest request; - - public DelegateHttpRequest(HttpRequest request) { - super(request); - this.request = request; - } - - @Override - public HttpRequest setMethod(HttpMethod method) { - request.setMethod(method); - return this; - } - - @Override - public HttpRequest setUri(String uri) { - request.setUri(uri); - return this; - } - - @Override - @Deprecated - public HttpMethod getMethod() { - return request.method(); - } - - @Override - public HttpMethod method() { - return request.method(); - } - - @Override - @Deprecated - public String getUri() { - return request.uri(); - } - - @Override - public String uri() { - return request.uri(); - } - - @Override - public HttpRequest setProtocolVersion(HttpVersion version) { - super.setProtocolVersion(version); - return this; - } -} diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateHttpResponse.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateHttpResponse.java deleted file mode 100644 index 0f73b78..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateHttpResponse.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; - -class DelegateHttpResponse extends DelegateHttpMessage implements HttpResponse { - - protected final HttpResponse response; - - public DelegateHttpResponse(HttpResponse response) { - super(response); - this.response = response; - } - - @Override - public HttpResponse setStatus(HttpResponseStatus status) { - response.setStatus(status); - return this; - } - - @Override - @Deprecated - public HttpResponseStatus getStatus() { - return response.status(); - } - - @Override - public HttpResponseStatus status() { - return response.status(); - } - - @Override - public HttpResponse setProtocolVersion(HttpVersion version) { - super.setProtocolVersion(version); - return this; - } -} diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateStreamedHttpRequest.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateStreamedHttpRequest.java deleted file mode 100644 index cac840b..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateStreamedHttpRequest.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpRequest; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; - -final class DelegateStreamedHttpRequest extends DelegateHttpRequest implements StreamedHttpRequest { - - private final Publisher stream; - - public DelegateStreamedHttpRequest(HttpRequest request, Publisher stream) { - super(request); - this.stream = stream; - } - - @Override - public void subscribe(Subscriber subscriber) { - stream.subscribe(subscriber); - } -} diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateStreamedHttpResponse.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateStreamedHttpResponse.java deleted file mode 100644 index 0609ad9..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/DelegateStreamedHttpResponse.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpResponse; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; - -final class DelegateStreamedHttpResponse extends DelegateHttpResponse implements StreamedHttpResponse { - - private final Publisher stream; - - public DelegateStreamedHttpResponse(HttpResponse response, Publisher stream) { - super(response); - this.stream = stream; - } - - @Override - public void subscribe(Subscriber subscriber) { - stream.subscribe(subscriber); - } -} diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/EmptyHttpRequest.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/EmptyHttpRequest.java deleted file mode 100644 index e17efc1..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/EmptyHttpRequest.java +++ /dev/null @@ -1,135 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultHttpHeaders; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.ReferenceCounted; - -class EmptyHttpRequest extends DelegateHttpRequest implements FullHttpRequest { - - public EmptyHttpRequest(HttpRequest request) { - super(request); - } - - @Override - public FullHttpRequest setUri(String uri) { - super.setUri(uri); - return this; - } - - @Override - public FullHttpRequest setMethod(HttpMethod method) { - super.setMethod(method); - return this; - } - - @Override - public FullHttpRequest setProtocolVersion(HttpVersion version) { - super.setProtocolVersion(version); - return this; - } - - @Override - public FullHttpRequest copy() { - if (request instanceof FullHttpRequest) { - return new EmptyHttpRequest(((FullHttpRequest) request).copy()); - } else { - DefaultHttpRequest copy = new DefaultHttpRequest(protocolVersion(), method(), uri()); - copy.headers().set(headers()); - return new EmptyHttpRequest(copy); - } - } - - @Override - public FullHttpRequest retain(int increment) { - ReferenceCountUtil.retain(message, increment); - return this; - } - - @Override - public FullHttpRequest retain() { - ReferenceCountUtil.retain(message); - return this; - } - - @Override - public FullHttpRequest touch() { - if (request instanceof FullHttpRequest) { - return ((FullHttpRequest) request).touch(); - } else { - return this; - } - } - - @Override - public FullHttpRequest touch(Object o) { - if (request instanceof FullHttpRequest) { - return ((FullHttpRequest) request).touch(o); - } else { - return this; - } - } - - @Override - public HttpHeaders trailingHeaders() { - return new DefaultHttpHeaders(); - } - - @Override - public FullHttpRequest duplicate() { - if (request instanceof FullHttpRequest) { - return ((FullHttpRequest) request).duplicate(); - } else { - return this; - } - } - - @Override - public FullHttpRequest retainedDuplicate() { - if (request instanceof FullHttpRequest) { - return ((FullHttpRequest) request).retainedDuplicate(); - } else { - return this; - } - } - - @Override - public FullHttpRequest replace(ByteBuf byteBuf) { - if (message instanceof FullHttpRequest) { - return ((FullHttpRequest) request).replace(byteBuf); - } else { - return this; - } - } - - @Override - public ByteBuf content() { - return Unpooled.EMPTY_BUFFER; - } - - @Override - public int refCnt() { - if (message instanceof ReferenceCounted) { - return ((ReferenceCounted) message).refCnt(); - } else { - return 1; - } - } - - @Override - public boolean release() { - return ReferenceCountUtil.release(message); - } - - @Override - public boolean release(int decrement) { - return ReferenceCountUtil.release(message, decrement); - } -} diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/EmptyHttpResponse.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/EmptyHttpResponse.java deleted file mode 100644 index a663cb4..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/EmptyHttpResponse.java +++ /dev/null @@ -1,129 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultHttpHeaders; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.ReferenceCounted; - -class EmptyHttpResponse extends DelegateHttpResponse implements FullHttpResponse { - - public EmptyHttpResponse(HttpResponse response) { - super(response); - } - - @Override - public FullHttpResponse setStatus(HttpResponseStatus status) { - super.setStatus(status); - return this; - } - - @Override - public FullHttpResponse setProtocolVersion(HttpVersion version) { - super.setProtocolVersion(version); - return this; - } - - @Override - public FullHttpResponse copy() { - if (response instanceof FullHttpResponse) { - return new EmptyHttpResponse(((FullHttpResponse) response).copy()); - } else { - DefaultHttpResponse copy = new DefaultHttpResponse(protocolVersion(), status()); - copy.headers().set(headers()); - return new EmptyHttpResponse(copy); - } - } - - @Override - public FullHttpResponse retain(int increment) { - ReferenceCountUtil.retain(message, increment); - return this; - } - - @Override - public FullHttpResponse retain() { - ReferenceCountUtil.retain(message); - return this; - } - - @Override - public FullHttpResponse touch() { - if (response instanceof FullHttpResponse) { - return ((FullHttpResponse) response).touch(); - } else { - return this; - } - } - - @Override - public FullHttpResponse touch(Object o) { - if (response instanceof FullHttpResponse) { - return ((FullHttpResponse) response).touch(o); - } else { - return this; - } - } - - @Override - public HttpHeaders trailingHeaders() { - return new DefaultHttpHeaders(); - } - - @Override - public FullHttpResponse duplicate() { - if (response instanceof FullHttpResponse) { - return ((FullHttpResponse) response).duplicate(); - } else { - return this; - } - } - - @Override - public FullHttpResponse retainedDuplicate() { - if (response instanceof FullHttpResponse) { - return ((FullHttpResponse) response).retainedDuplicate(); - } else { - return this; - } - } - - @Override - public FullHttpResponse replace(ByteBuf byteBuf) { - if (response instanceof FullHttpResponse) { - return ((FullHttpResponse) response).replace(byteBuf); - } else { - return this; - } - } - - @Override - public ByteBuf content() { - return Unpooled.EMPTY_BUFFER; - } - - @Override - public int refCnt() { - if (message instanceof ReferenceCounted) { - return ((ReferenceCounted) message).refCnt(); - } else { - return 1; - } - } - - @Override - public boolean release() { - return ReferenceCountUtil.release(message); - } - - @Override - public boolean release(int decrement) { - return ReferenceCountUtil.release(message, decrement); - } -} diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HandlerPublisher.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HandlerPublisher.java deleted file mode 100644 index b6f6526..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HandlerPublisher.java +++ /dev/null @@ -1,468 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandler; -import io.netty.channel.ChannelPipeline; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.internal.TypeParameterMatcher; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -import java.util.LinkedList; -import java.util.Queue; -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.xbib.netty.http.server.reactive.HandlerPublisher.State.BUFFERING; -import static org.xbib.netty.http.server.reactive.HandlerPublisher.State.DEMANDING; -import static org.xbib.netty.http.server.reactive.HandlerPublisher.State.DONE; -import static org.xbib.netty.http.server.reactive.HandlerPublisher.State.DRAINING; -import static org.xbib.netty.http.server.reactive.HandlerPublisher.State.IDLE; -import static org.xbib.netty.http.server.reactive.HandlerPublisher.State.NO_CONTEXT; -import static org.xbib.netty.http.server.reactive.HandlerPublisher.State.NO_SUBSCRIBER; -import static org.xbib.netty.http.server.reactive.HandlerPublisher.State.NO_SUBSCRIBER_ERROR; -import static org.xbib.netty.http.server.reactive.HandlerPublisher.State.NO_SUBSCRIBER_OR_CONTEXT; - -/** - * Publisher for a Netty Handler. - * - * This publisher supports only one subscriber. - * - * All interactions with the subscriber are done from the handlers executor, hence, they provide the same happens before - * semantics that Netty provides. - * - * The handler publishes all messages that match the type as specified by the passed in class. Any non matching messages - * are forwarded to the next handler. - * - * The publisher will signal complete if it receives a channel inactive event. - * - * The publisher will release any messages that it drops (for example, messages that are buffered when the subscriber - * cancels), but other than that, it does not release any messages. It is up to the subscriber to release messages. - * - * If the subscriber cancels, the publisher will send a close event up the channel pipeline. - * - * All errors will short circuit the buffer, and cause publisher to immediately call the subscribers onError method, - * dropping the buffer. - * - * The publisher can be subscribed to or placed in a handler chain in any order. - */ -public class HandlerPublisher extends ChannelDuplexHandler implements Publisher { - - private final EventExecutor executor; - private final TypeParameterMatcher matcher; - - /** - * Create a handler publisher. - * - * The supplied executor must be the same event loop as the event loop that this handler is eventually registered - * with, if not, an exception will be thrown when the handler is registered. - * - * @param executor The executor to execute asynchronous events from the subscriber on. - * @param subscriberMessageType The type of message this publisher accepts. - */ - public HandlerPublisher(EventExecutor executor, Class subscriberMessageType) { - this.executor = executor; - this.matcher = TypeParameterMatcher.get(subscriberMessageType); - } - - /** - * Returns {@code true} if the given message should be handled. If {@code false} it will be passed to the next - * {@link ChannelInboundHandler} in the {@link ChannelPipeline}. - * - * @param msg The message to check. - * @return True if the message should be accepted. - */ - protected boolean acceptInboundMessage(Object msg) { - return matcher.match(msg); - } - - /** - * Override to handle when a subscriber cancels the subscription. - * - * By default, this method will simply close the channel. - */ - protected void cancelled() { - ctx.close(); - } - - /** - * Override to intercept when demand is requested. - * - * By default, a channel read is invoked. - */ - protected void requestDemand() { - ctx.read(); - } - - enum State { - /** - * Initial state. There's no subscriber, and no context. - */ - NO_SUBSCRIBER_OR_CONTEXT, - - /** - * A subscriber has been provided, but no context has been provided. - */ - NO_CONTEXT, - - /** - * A context has been provided, but no subscriber has been provided. - */ - NO_SUBSCRIBER, - - /** - * An error has been received, but there's no subscriber to receive it. - */ - NO_SUBSCRIBER_ERROR, - - /** - * There is no demand, and we have nothing buffered. - */ - IDLE, - - /** - * There is no demand, and we're buffering elements. - */ - BUFFERING, - - /** - * We have nothing buffered, but there is demand. - */ - DEMANDING, - - /** - * The stream is complete, however there are still elements buffered for which no demand has come from the subscriber. - */ - DRAINING, - - /** - * We're done, in the terminal state. - */ - DONE - } - - private final Queue buffer = new LinkedList<>(); - - /** - * Whether a subscriber has been provided. This is used to detect whether two subscribers are subscribing - * simultaneously. - */ - private final AtomicBoolean hasSubscriber = new AtomicBoolean(); - - private State state = NO_SUBSCRIBER_OR_CONTEXT; - - private volatile Subscriber subscriber; - - private ChannelHandlerContext ctx; - - private long outstandingDemand = 0; - - private Throwable noSubscriberError; - - @Override - public void subscribe(final Subscriber subscriber) { - if (subscriber == null) { - throw new NullPointerException("Null subscriber"); - } - if (!hasSubscriber.compareAndSet(false, true)) { - subscriber.onSubscribe(new Subscription() { - @Override - public void request(long n) { - } - @Override - public void cancel() { - } - }); - subscriber.onError(new IllegalStateException("This publisher only supports one subscriber")); - } else { - executor.execute(() -> provideSubscriber(subscriber)); - } - } - - private void provideSubscriber(Subscriber subscriber) { - this.subscriber = subscriber; - switch (state) { - case NO_SUBSCRIBER_OR_CONTEXT: - state = NO_CONTEXT; - break; - case NO_SUBSCRIBER: - if (buffer.isEmpty()) { - state = IDLE; - } else { - state = BUFFERING; - } - subscriber.onSubscribe(new ChannelSubscription()); - break; - case DRAINING: - subscriber.onSubscribe(new ChannelSubscription()); - break; - case NO_SUBSCRIBER_ERROR: - cleanup(); - state = DONE; - subscriber.onSubscribe(new ChannelSubscription()); - subscriber.onError(noSubscriberError); - break; - } - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) { - if (ctx.channel().isRegistered()) { - provideChannelContext(ctx); - } - } - - @Override - public void channelRegistered(ChannelHandlerContext ctx) { - provideChannelContext(ctx); - ctx.fireChannelRegistered(); - } - - private void provideChannelContext(ChannelHandlerContext ctx) { - switch(state) { - case NO_SUBSCRIBER_OR_CONTEXT: - verifyRegisteredWithRightExecutor(ctx); - this.ctx = ctx; - state = NO_SUBSCRIBER; - break; - case NO_CONTEXT: - verifyRegisteredWithRightExecutor(ctx); - this.ctx = ctx; - state = IDLE; - subscriber.onSubscribe(new ChannelSubscription()); - break; - default: - break; - } - } - - private void verifyRegisteredWithRightExecutor(ChannelHandlerContext ctx) { - if (!executor.inEventLoop()) { - throw new IllegalArgumentException("Channel handler MUST be registered with the same EventExecutor that it is created with."); - } - } - - @Override - public void channelActive(ChannelHandlerContext ctx) { - if (state == DEMANDING) { - requestDemand(); - } - ctx.fireChannelActive(); - } - - private void receivedDemand(long demand) { - switch (state) { - case BUFFERING: - case DRAINING: - if (addDemand(demand)) { - flushBuffer(); - } - break; - - case DEMANDING: - addDemand(demand); - break; - - case IDLE: - if (addDemand(demand)) { - state = DEMANDING; - requestDemand(); - } - break; - default: - break; - } - } - - private boolean addDemand(long demand) { - if (demand <= 0) { - illegalDemand(); - return false; - } else { - if (outstandingDemand < Long.MAX_VALUE) { - outstandingDemand += demand; - if (outstandingDemand < 0) { - outstandingDemand = Long.MAX_VALUE; - } - } - return true; - } - } - - private void illegalDemand() { - cleanup(); - subscriber.onError(new IllegalArgumentException("Request for 0 or negative elements in violation of Section 3.9 of the Reactive Streams specification")); - ctx.close(); - state = DONE; - } - - private void flushBuffer() { - while (!buffer.isEmpty() && outstandingDemand > 0) { - publishMessage(buffer.remove()); - } - if (buffer.isEmpty()) { - if (outstandingDemand > 0) { - if (state == BUFFERING) { - state = DEMANDING; - } - requestDemand(); - } else if (state == BUFFERING) { - state = IDLE; - } - } - } - - private void receivedCancel() { - switch (state) { - case BUFFERING: - case DEMANDING: - case IDLE: - cancelled(); - // fall through - case DRAINING: - state = DONE; - break; - } - cleanup(); - subscriber = null; - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object message) throws Exception { - if (acceptInboundMessage(message)) { - switch (state) { - case IDLE: - buffer.add(message); - state = BUFFERING; - break; - case NO_SUBSCRIBER: - case BUFFERING: - buffer.add(message); - break; - case DEMANDING: - publishMessage(message); - break; - case DRAINING: - case DONE: - ReferenceCountUtil.release(message); - break; - case NO_CONTEXT: - case NO_SUBSCRIBER_OR_CONTEXT: - throw new IllegalStateException("Message received before added to the channel context"); - } - } else { - ctx.fireChannelRead(message); - } - } - - private void publishMessage(Object message) { - if (COMPLETE.equals(message)) { - subscriber.onComplete(); - state = DONE; - } else { - @SuppressWarnings("unchecked") - T next = (T) message; - subscriber.onNext(next); - if (outstandingDemand < Long.MAX_VALUE) { - outstandingDemand--; - if (outstandingDemand == 0 && state != DRAINING) { - if (buffer.isEmpty()) { - state = IDLE; - } else { - state = BUFFERING; - } - } - } - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - if (state == DEMANDING) { - requestDemand(); - } - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) { - complete(); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) { - complete(); - } - - private void complete() { - switch (state) { - case NO_SUBSCRIBER: - case BUFFERING: - buffer.add(COMPLETE); - state = DRAINING; - break; - case DEMANDING: - case IDLE: - subscriber.onComplete(); - state = DONE; - break; - case NO_SUBSCRIBER_ERROR: - break; - default: - break; - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - switch (state) { - case NO_SUBSCRIBER: - noSubscriberError = cause; - state = NO_SUBSCRIBER_ERROR; - cleanup(); - break; - case BUFFERING: - case DEMANDING: - case IDLE: - case DRAINING: - state = DONE; - cleanup(); - subscriber.onError(cause); - break; - default: - break; - } - } - - /** - * Release all elements from the buffer. - */ - private void cleanup() { - while (!buffer.isEmpty()) { - ReferenceCountUtil.release(buffer.remove()); - } - } - - private class ChannelSubscription implements Subscription { - @Override - public void request(final long demand) { - executor.execute(() -> receivedDemand(demand)); - } - - @Override - public void cancel() { - executor.execute(HandlerPublisher.this::receivedCancel); - } - } - - /** - * Used for buffering a completion signal. - */ - private static final Object COMPLETE = new Object() { - @Override - public String toString() { - return "COMPLETE"; - } - }; -} diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HandlerSubscriber.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HandlerSubscriber.java deleted file mode 100644 index 12a5697..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HandlerSubscriber.java +++ /dev/null @@ -1,266 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.EventExecutor; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.xbib.netty.http.server.reactive.HandlerSubscriber.State.CANCELLED; -import static org.xbib.netty.http.server.reactive.HandlerSubscriber.State.COMPLETE; -import static org.xbib.netty.http.server.reactive.HandlerSubscriber.State.INACTIVE; -import static org.xbib.netty.http.server.reactive.HandlerSubscriber.State.NO_CONTEXT; -import static org.xbib.netty.http.server.reactive.HandlerSubscriber.State.NO_SUBSCRIPTION; -import static org.xbib.netty.http.server.reactive.HandlerSubscriber.State.NO_SUBSCRIPTION_OR_CONTEXT; -import static org.xbib.netty.http.server.reactive.HandlerSubscriber.State.RUNNING; - - -/** - * Subscriber that publishes received messages to the handler pipeline. - */ -public class HandlerSubscriber extends ChannelDuplexHandler implements Subscriber { - - private static final long DEFAULT_LOW_WATERMARK = 4; - - private static final long DEFAULT_HIGH_WATERMARK = 16; - - /** - * Create a new handler subscriber. - * - * The supplied executor must be the same event loop as the event loop that this handler is eventually registered - * with, if not, an exception will be thrown when the handler is registered. - * - * @param executor The executor to execute asynchronous events from the publisher on. - * @param demandLowWatermark The low watermark for demand. When demand drops below this, more will be requested. - * @param demandHighWatermark The high watermark for demand. This is the maximum that will be requested. - */ - public HandlerSubscriber(EventExecutor executor, long demandLowWatermark, long demandHighWatermark) { - this.executor = executor; - this.demandLowWatermark = demandLowWatermark; - this.demandHighWatermark = demandHighWatermark; - } - - /** - * Create a new handler subscriber with the default low and high watermarks. - * - * The supplied executor must be the same event loop as the event loop that this handler is eventually registered - * with, if not, an exception will be thrown when the handler is registered. - * - * @param executor The executor to execute asynchronous events from the publisher on. - * @see #HandlerSubscriber(EventExecutor, long, long) - */ - public HandlerSubscriber(EventExecutor executor) { - this(executor, DEFAULT_LOW_WATERMARK, DEFAULT_HIGH_WATERMARK); - } - - /** - * Override for custom error handling. By default, it closes the channel. - * - * @param error The error to handle. - */ - protected void error(Throwable error) { - doClose(); - } - - /** - * Override for custom completion handling. By default, it closes the channel. - */ - protected void complete() { - doClose(); - } - - private final EventExecutor executor; - private final long demandLowWatermark; - private final long demandHighWatermark; - - enum State { - NO_SUBSCRIPTION_OR_CONTEXT, - NO_SUBSCRIPTION, - NO_CONTEXT, - INACTIVE, - RUNNING, - CANCELLED, - COMPLETE - } - - private final AtomicBoolean hasSubscription = new AtomicBoolean(); - - private volatile Subscription subscription; - private volatile ChannelHandlerContext ctx; - - private State state = NO_SUBSCRIPTION_OR_CONTEXT; - private long outstandingDemand = 0; - private ChannelFuture lastWriteFuture; - - @Override - public void handlerAdded(ChannelHandlerContext ctx) { - verifyRegisteredWithRightExecutor(ctx); - - switch (state) { - case NO_SUBSCRIPTION_OR_CONTEXT: - this.ctx = ctx; - state = NO_SUBSCRIPTION; - break; - case NO_CONTEXT: - this.ctx = ctx; - maybeStart(); - break; - case COMPLETE: - state = COMPLETE; - ctx.close(); - break; - default: - throw new IllegalStateException("This handler must only be added to a pipeline once " + state); - } - } - - @Override - public void channelRegistered(ChannelHandlerContext ctx) { - verifyRegisteredWithRightExecutor(ctx); - ctx.fireChannelRegistered(); - } - - private void verifyRegisteredWithRightExecutor(ChannelHandlerContext ctx) { - if (ctx.channel().isRegistered() && !executor.inEventLoop()) { - throw new IllegalArgumentException("Channel handler MUST be registered with the same EventExecutor that it is created with."); - } - } - - @Override - public void channelWritabilityChanged(ChannelHandlerContext ctx) { - maybeRequestMore(); - ctx.fireChannelWritabilityChanged(); - } - - @Override - public void channelActive(ChannelHandlerContext ctx) { - if (state == INACTIVE) { - state = RUNNING; - maybeRequestMore(); - } - ctx.fireChannelActive(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) { - cancel(); - ctx.fireChannelInactive(); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) { - cancel(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cancel(); - ctx.fireExceptionCaught(cause); - } - - private void cancel() { - switch (state) { - case NO_SUBSCRIPTION: - state = CANCELLED; - break; - case RUNNING: - case INACTIVE: - subscription.cancel(); - state = CANCELLED; - break; - default: - break; - } - } - - @Override - public void onSubscribe(final Subscription subscription) { - if (subscription == null) { - throw new NullPointerException("Null subscription"); - } else if (!hasSubscription.compareAndSet(false, true)) { - subscription.cancel(); - } else { - this.subscription = subscription; - executor.execute(this::provideSubscription); - } - } - - private void provideSubscription() { - switch (state) { - case NO_SUBSCRIPTION_OR_CONTEXT: - state = NO_CONTEXT; - break; - case NO_SUBSCRIPTION: - maybeStart(); - break; - case CANCELLED: - subscription.cancel(); - break; - default: - break; - } - } - - private void maybeStart() { - if (ctx.channel().isActive()) { - state = RUNNING; - maybeRequestMore(); - } else { - state = INACTIVE; - } - } - - @Override - public void onNext(T t) { - lastWriteFuture = ctx.writeAndFlush(t); - lastWriteFuture.addListener((ChannelFutureListener) future -> { - outstandingDemand--; - maybeRequestMore(); - }); - } - - @Override - public void onError(final Throwable error) { - if (error == null) { - throw new NullPointerException("Null error published"); - } - error(error); - } - - @Override - public void onComplete() { - if (lastWriteFuture == null) { - complete(); - } else { - lastWriteFuture.addListener((ChannelFutureListener) channelFuture -> complete()); - } - } - - private void doClose() { - executor.execute(() -> { - switch (state) { - case NO_SUBSCRIPTION: - case INACTIVE: - case RUNNING: - ctx.close(); - state = COMPLETE; - break; - default: - break; - - } - }); - } - - private void maybeRequestMore() { - if (outstandingDemand <= demandLowWatermark && ctx.channel().isWritable()) { - long toRequest = demandHighWatermark - outstandingDemand; - outstandingDemand = demandHighWatermark; - subscription.request(toRequest); - } - } -} diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HttpStreamsClientHandler.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HttpStreamsClientHandler.java deleted file mode 100644 index 0b9a66b..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HttpStreamsClientHandler.java +++ /dev/null @@ -1,152 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.util.ReferenceCountUtil; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -/** - * Handler that converts written {@link StreamedHttpRequest} messages into {@link HttpRequest} messages - * followed by {@link HttpContent} messages and reads {@link HttpResponse} messages followed by - * {@link HttpContent} messages and produces {@link StreamedHttpResponse} messages. - * - * This allows request and response bodies to be handled using reactive streams. - * - * There are two types of messages that this handler accepts for writing, {@link StreamedHttpRequest} and - * {@link FullHttpRequest}. Writing any other messages may potentially lead to HTTP message mangling. - * - * There are two types of messages that this handler will send down the chain, {@link StreamedHttpResponse}, - * and {@link FullHttpResponse}. If {@link io.netty.channel.ChannelOption#AUTO_READ} is false for the channel, - * then any {@link StreamedHttpResponse} messages must be subscribed to consume the body, otherwise - * it's possible that no read will be done of the messages. - * - * As long as messages are returned in the order that they arrive, this handler implicitly supports HTTP - * pipelining. - */ -public class HttpStreamsClientHandler extends HttpStreamsHandler { - - private int inFlight = 0; - private int withServer = 0; - private ChannelPromise closeOnZeroInFlight = null; - private Subscriber awaiting100Continue; - private StreamedHttpMessage awaiting100ContinueMessage; - private boolean ignoreResponseBody = false; - - public HttpStreamsClientHandler() { - super(HttpResponse.class, HttpRequest.class); - } - - @Override - protected boolean hasBody(HttpResponse response) { - if (response.status().code() >= 100 && response.status().code() < 200) { - return false; - } - if (response.status().equals(HttpResponseStatus.NO_CONTENT) || - response.status().equals(HttpResponseStatus.NOT_MODIFIED)) { - return false; - } - if (HttpUtil.isTransferEncodingChunked(response)) { - return true; - } - if (HttpUtil.isContentLengthSet(response)) { - return HttpUtil.getContentLength(response) > 0; - } - return true; - } - - @Override - public void close(ChannelHandlerContext ctx, ChannelPromise future) throws Exception { - if (inFlight == 0) { - ctx.close(future); - } else { - closeOnZeroInFlight = future; - } - } - - @Override - protected void consumedInMessage(ChannelHandlerContext ctx) { - inFlight--; - withServer--; - if (inFlight == 0 && closeOnZeroInFlight != null) { - ctx.close(closeOnZeroInFlight); - } - } - - @Override - protected void receivedOutMessage(ChannelHandlerContext ctx) { - inFlight++; - } - - @Override - protected void sentOutMessage(ChannelHandlerContext ctx) { - withServer++; - } - - @Override - protected HttpResponse createEmptyMessage(HttpResponse response) { - return new EmptyHttpResponse(response); - } - - @Override - protected HttpResponse createStreamedMessage(HttpResponse response, Publisher stream) { - return new DelegateStreamedHttpResponse(response, stream); - } - - @Override - protected void subscribeSubscriberToStream(StreamedHttpMessage msg, Subscriber subscriber) { - if (HttpUtil.is100ContinueExpected(msg)) { - awaiting100Continue = subscriber; - awaiting100ContinueMessage = msg; - } else { - super.subscribeSubscriberToStream(msg, subscriber); - } - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - - if (msg instanceof HttpResponse && awaiting100Continue != null && withServer == 0) { - HttpResponse response = (HttpResponse) msg; - if (response.status().equals(HttpResponseStatus.CONTINUE)) { - super.subscribeSubscriberToStream(awaiting100ContinueMessage, awaiting100Continue); - awaiting100Continue = null; - awaiting100ContinueMessage = null; - if (msg instanceof FullHttpResponse) { - ReferenceCountUtil.release(msg); - } else { - ignoreResponseBody = true; - } - } else { - awaiting100ContinueMessage.subscribe(new CancelledSubscriber<>()); - awaiting100ContinueMessage = null; - awaiting100Continue.onSubscribe(new Subscription() { - public void request(long n) { - } - public void cancel() { - } - }); - awaiting100Continue.onComplete(); - awaiting100Continue = null; - super.channelRead(ctx, msg); - } - } else if (ignoreResponseBody && msg instanceof HttpContent) { - - ReferenceCountUtil.release(msg); - if (msg instanceof LastHttpContent) { - ignoreResponseBody = false; - } - } else { - super.channelRead(ctx, msg); - } - } -} diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HttpStreamsHandler.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HttpStreamsHandler.java deleted file mode 100644 index 57b70d4..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HttpStreamsHandler.java +++ /dev/null @@ -1,321 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.handler.codec.http.FullHttpMessage; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.util.ReferenceCountUtil; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; - -import java.util.LinkedList; -import java.util.Queue; - -abstract class HttpStreamsHandler extends ChannelDuplexHandler { - - private final Queue outgoing = new LinkedList<>(); - - private final Class inClass; - - private final Class outClass; - - public HttpStreamsHandler(Class inClass, Class outClass) { - this.inClass = inClass; - this.outClass = outClass; - } - - /** - * The incoming message that is currently being streamed out to a subscriber. - * - * This is tracked so that if its subscriber cancels, we can go into a mode where we ignore the rest of the body. - * Since subscribers may cancel as many times as they like, including well after they've received all their content, - * we need to track what the current message that's being streamed out is so that we can ignore it if it's not - * currently being streamed out. - */ - private In currentlyStreamedMessage; - - /** - * Ignore the remaining reads for the incoming message. - * - * This is used in conjunction with currentlyStreamedMessage, as well as in situations where we have received the - * full body, but still might be expecting a last http content message. - */ - private boolean ignoreBodyRead; - - /** - * Whether a LastHttpContent message needs to be written once the incoming publisher completes. - * - * Since the publisher may itself publish a LastHttpContent message, we need to track this fact, because if it - * doesn't, then we need to write one ourselves. - */ - private boolean sendLastHttpContent; - - /** - * Whether the given incoming message has a body. - * @param in input - * @return true if message has a body - */ - protected abstract boolean hasBody(In in); - - /** - * Create an empty incoming message. This must be of type FullHttpMessage, and is invoked when we've determined - * that an incoming message can't have a body, so we send it on as a FullHttpMessage. - * @param in input - * @return incoming message - */ - protected abstract In createEmptyMessage(In in); - - /** - * Create a streamed incoming message with the given stream. - * @param in input - * @param stream stream - * @return input - */ - protected abstract In createStreamedMessage(In in, Publisher stream); - - /** - * Invoked when an incoming message is first received. - * - * Overridden by sub classes for state tracking. - * @param ctx channel handler context - */ - protected void receivedInMessage(ChannelHandlerContext ctx) { - } - - /** - * Invoked when an incoming message is fully consumed. - * - * Overridden by sub classes for state tracking. - * @param ctx channel handler context - */ - protected void consumedInMessage(ChannelHandlerContext ctx) { - } - - /** - * Invoked when an outgoing message is first received. - * - * Overridden by sub classes for state tracking. - * @param ctx channel handler context - */ - protected void receivedOutMessage(ChannelHandlerContext ctx) { - } - - /** - * Invoked when an outgoing message is fully sent. - * - * Overridden by sub classes for state tracking. - * @param ctx channel handler context - */ - protected void sentOutMessage(ChannelHandlerContext ctx) { - } - - /** - * Subscribe the given subscriber to the given streamed message. - * - * Provided so that the client subclass can intercept this to hold off sending the body of an expect 100 continue - * request. - * @param msg msg - * @param subscriber subscriber - */ - protected void subscribeSubscriberToStream(StreamedHttpMessage msg, Subscriber subscriber) { - msg.subscribe(subscriber); - } - - /** - * Invoked every time a read of the incoming body is requested by the subscriber. - * - * Provided so that the server subclass can intercept this to send a 100 continue response. - * @param ctx channel handler context - */ - protected void bodyRequested(ChannelHandlerContext ctx) {} - - @Override - public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception { - if (inClass.isInstance(msg)) { - receivedInMessage(ctx); - final In inMsg = inClass.cast(msg); - if (inMsg instanceof FullHttpMessage) { - ctx.fireChannelRead(inMsg); - consumedInMessage(ctx); - } else if (!hasBody(inMsg)) { - ctx.fireChannelRead(createEmptyMessage(inMsg)); - consumedInMessage(ctx); - ignoreBodyRead = true; - } else { - currentlyStreamedMessage = inMsg; - HandlerPublisher publisher = new HandlerPublisher<>(ctx.executor(), HttpContent.class) { - @Override - protected void cancelled() { - if (ctx.executor().inEventLoop()) { - handleCancelled(ctx, inMsg); - } else { - ctx.executor().execute(() -> handleCancelled(ctx, inMsg)); - } - } - - @Override - protected void requestDemand() { - bodyRequested(ctx); - super.requestDemand(); - } - }; - ctx.channel().pipeline().addAfter(ctx.name(), ctx.name() + "-body-publisher", publisher); - ctx.fireChannelRead(createStreamedMessage(inMsg, publisher)); - } - } else if (msg instanceof HttpContent) { - handleReadHttpContent(ctx, (HttpContent) msg); - } - } - - private void handleCancelled(ChannelHandlerContext ctx, In msg) { - if (currentlyStreamedMessage == msg) { - ignoreBodyRead = true; - ctx.read(); - } - } - - private void handleReadHttpContent(ChannelHandlerContext ctx, HttpContent content) { - if (!ignoreBodyRead) { - if (content instanceof LastHttpContent) { - if (content.content().readableBytes() > 0 || - !((LastHttpContent) content).trailingHeaders().isEmpty()) { - ctx.fireChannelRead(content); - } else { - ReferenceCountUtil.release(content); - } - removeHandlerIfActive(ctx, ctx.name() + "-body-publisher"); - currentlyStreamedMessage = null; - consumedInMessage(ctx); - } else { - ctx.fireChannelRead(content); - } - } else { - ReferenceCountUtil.release(content); - if (content instanceof LastHttpContent) { - ignoreBodyRead = false; - if (currentlyStreamedMessage != null) { - removeHandlerIfActive(ctx, ctx.name() + "-body-publisher"); - } - currentlyStreamedMessage = null; - } - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - if (ignoreBodyRead) { - ctx.read(); - } else { - ctx.fireChannelReadComplete(); - } - } - - @Override - public void write(final ChannelHandlerContext ctx, Object msg, final ChannelPromise promise) { - if (outClass.isInstance(msg)) { - Outgoing out = new Outgoing(outClass.cast(msg), promise); - receivedOutMessage(ctx); - if (outgoing.isEmpty()) { - outgoing.add(out); - flushNext(ctx); - } else { - outgoing.add(out); - } - } else if (msg instanceof LastHttpContent) { - sendLastHttpContent = false; - ctx.write(msg, promise); - } else { - ctx.write(msg, promise); - } - } - - protected void unbufferedWrite(final ChannelHandlerContext ctx, final Outgoing out) { - if (out.message instanceof FullHttpMessage) { - ctx.writeAndFlush(out.message, out.promise); - out.promise.addListener((ChannelFutureListener) channelFuture -> - executeInEventLoop(ctx, () -> { - sentOutMessage(ctx); - outgoing.remove(); - flushNext(ctx); - })); - } else if (out.message instanceof StreamedHttpMessage) { - StreamedHttpMessage streamed = (StreamedHttpMessage) out.message; - HandlerSubscriber subscriber = new HandlerSubscriber<>(ctx.executor()) { - @Override - protected void error(Throwable error) { - out.promise.tryFailure(error); - ctx.close(); - } - - @Override - protected void complete() { - executeInEventLoop(ctx, () -> completeBody(ctx)); - } - }; - sendLastHttpContent = true; - ctx.writeAndFlush(out.message); - ctx.pipeline().addAfter(ctx.name(), ctx.name() + "-body-subscriber", subscriber); - subscribeSubscriberToStream(streamed, subscriber); - } - } - - private void completeBody(final ChannelHandlerContext ctx) { - removeHandlerIfActive(ctx, ctx.name() + "-body-subscriber"); - if (sendLastHttpContent) { - ChannelPromise promise = outgoing.peek().promise; - ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT, promise).addListener( - (ChannelFutureListener) channelFuture -> executeInEventLoop(ctx, () -> { - outgoing.remove(); - sentOutMessage(ctx); - flushNext(ctx); - }) - ); - } else { - outgoing.remove().promise.setSuccess(); - sentOutMessage(ctx); - flushNext(ctx); - } - } - - /** - * Most operations we want to do even if the channel is not active, because if it's not, then we want to encounter - * the error that occurs when that operation happens and so that it can be passed up to the user. However, removing - * handlers should only be done if the channel is active, because the error that is encountered when they aren't - * makes no sense to the user (NoSuchElementException). - */ - private void removeHandlerIfActive(ChannelHandlerContext ctx, String name) { - if (ctx.channel().isActive()) { - ctx.pipeline().remove(name); - } - } - - private void flushNext(ChannelHandlerContext ctx) { - if (!outgoing.isEmpty()) { - unbufferedWrite(ctx, outgoing.element()); - } else { - ctx.fireChannelWritabilityChanged(); - } - } - - private void executeInEventLoop(ChannelHandlerContext ctx, Runnable runnable) { - if (ctx.executor().inEventLoop()) { - runnable.run(); - } else { - ctx.executor().execute(runnable); - } - } - - class Outgoing { - final Out message; - final ChannelPromise promise; - - Outgoing(Out message, ChannelPromise promise) { - this.message = message; - this.promise = promise; - } - } -} diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HttpStreamsServerHandler.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HttpStreamsServerHandler.java deleted file mode 100644 index ad053a1..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/HttpStreamsServerHandler.java +++ /dev/null @@ -1,217 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker; -import io.netty.handler.codec.http.websocketx.WebSocketVersion; -import org.reactivestreams.Publisher; - -import java.util.Collections; -import java.util.List; -import java.util.NoSuchElementException; - -/** - * Handler that reads {@link HttpRequest} messages followed by {@link HttpContent} messages and produces - * {@link StreamedHttpRequest} messages, and converts written {@link StreamedHttpResponse} messages into - * {@link HttpResponse} messages followed by {@link HttpContent} messages. - * - * This allows request and response bodies to be handled using reactive streams. - * - * There are two types of messages that this handler will send down the chain, {@link StreamedHttpRequest}, - * and {@link FullHttpRequest}. If {@link io.netty.channel.ChannelOption#AUTO_READ} is false for the channel, - * then any {@link StreamedHttpRequest} messages must be subscribed to consume the body, otherwise - * it's possible that no read will be done of the messages. - * - * There are three types of messages that this handler accepts for writing, {@link StreamedHttpResponse}, - * {@link WebSocketHttpResponse} and {@link FullHttpResponse}. Writing any other messages may potentially - * lead to HTTP message mangling. - * - * As long as messages are returned in the order that they arrive, this handler implicitly supports HTTP - * pipelining. - */ -public class HttpStreamsServerHandler extends HttpStreamsHandler { - - private HttpRequest lastRequest = null; - private Outgoing webSocketResponse = null; - private int inFlight = 0; - private boolean continueExpected = true; - private boolean sendContinue = false; - private boolean close = false; - - private final List dependentHandlers; - - public HttpStreamsServerHandler() { - this(Collections.emptyList()); - } - - /** - * Create a new handler that is depended on by the given handlers. - * - * The list of dependent handlers will be removed from the chain when this handler is removed from the chain, - * for example, when the connection is upgraded to use websockets. This is useful, for example, for removing - * the reactive streams publisher/subscriber from the chain in that event. - * - * @param dependentHandlers The handlers that depend on this handler. - */ - public HttpStreamsServerHandler(List dependentHandlers) { - super(HttpRequest.class, HttpResponse.class); - this.dependentHandlers = dependentHandlers; - } - - @Override - protected boolean hasBody(HttpRequest request) { - // Http requests don't have a body if they define 0 content length, or no content length and no transfer - // encoding - return HttpUtil.getContentLength(request, 0) != 0 || HttpUtil.isTransferEncodingChunked(request); - } - - @Override - protected HttpRequest createEmptyMessage(HttpRequest request) { - return new EmptyHttpRequest(request); - } - - @Override - protected HttpRequest createStreamedMessage(HttpRequest httpRequest, Publisher stream) { - return new DelegateStreamedHttpRequest(httpRequest, stream); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - continueExpected = false; - sendContinue = false; - if (msg instanceof HttpRequest) { - HttpRequest request = (HttpRequest) msg; - lastRequest = request; - if (HttpUtil.is100ContinueExpected(request)) { - continueExpected = true; - } - } - super.channelRead(ctx, msg); - } - - @Override - protected void receivedInMessage(ChannelHandlerContext ctx) { - inFlight++; - } - - @Override - protected void sentOutMessage(ChannelHandlerContext ctx) { - inFlight--; - if (inFlight == 1 && continueExpected && sendContinue) { - ctx.writeAndFlush(new DefaultFullHttpResponse(lastRequest.protocolVersion(), HttpResponseStatus.CONTINUE)); - sendContinue = false; - continueExpected = false; - } - if (close) { - ctx.close(); - } - } - - @Override - protected void unbufferedWrite(ChannelHandlerContext ctx, HttpStreamsHandler.Outgoing out) { - if (out.message instanceof WebSocketHttpResponse) { - if ((lastRequest instanceof FullHttpRequest) || !hasBody(lastRequest)) { - handleWebSocketResponse(ctx, out); - } else { - webSocketResponse = out; - } - } else { - String connection = out.message.headers().get(HttpHeaderNames.CONNECTION); - if (lastRequest.protocolVersion().isKeepAliveDefault()) { - if ("close".equalsIgnoreCase(connection)) { - close = true; - } - } else { - if (!"keep-alive".equalsIgnoreCase(connection)) { - close = true; - } - } - if (inFlight == 1 && continueExpected) { - HttpUtil.setKeepAlive(out.message, false); - close = true; - continueExpected = false; - } - if (!HttpUtil.isContentLengthSet(out.message) && !HttpUtil.isTransferEncodingChunked(out.message) - && canHaveBody(out.message)) { - HttpUtil.setKeepAlive(out.message, false); - close = true; - } - super.unbufferedWrite(ctx, out); - } - } - - private boolean canHaveBody(HttpResponse message) { - HttpResponseStatus status = message.status(); - return !(status == HttpResponseStatus.CONTINUE || status == HttpResponseStatus.SWITCHING_PROTOCOLS || - status == HttpResponseStatus.PROCESSING || status == HttpResponseStatus.NO_CONTENT || - status == HttpResponseStatus.NOT_MODIFIED); - } - - @Override - protected void consumedInMessage(ChannelHandlerContext ctx) { - if (webSocketResponse != null) { - handleWebSocketResponse(ctx, webSocketResponse); - webSocketResponse = null; - } - } - - private void handleWebSocketResponse(ChannelHandlerContext ctx, Outgoing out) { - WebSocketHttpResponse response = (WebSocketHttpResponse) out.message; - WebSocketServerHandshaker handshaker = response.handshakerFactory().newHandshaker(lastRequest); - if (handshaker == null) { - HttpResponse res = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, - HttpResponseStatus.UPGRADE_REQUIRED); - res.headers().set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue()); - HttpUtil.setContentLength(res, 0); - super.unbufferedWrite(ctx, new Outgoing(res, out.promise)); - response.subscribe(new CancelledSubscriber<>()); - } else { - ChannelPipeline pipeline = ctx.pipeline(); - HandlerPublisher publisher = new HandlerPublisher<>(ctx.executor(), WebSocketFrame.class); - HandlerSubscriber subscriber = new HandlerSubscriber<>(ctx.executor()); - pipeline.addAfter(ctx.executor(), ctx.name(), "websocket-subscriber", subscriber); - pipeline.addAfter(ctx.executor(), ctx.name(), "websocket-publisher", publisher); - ctx.pipeline().remove(ctx.name()); - handshaker.handshake(ctx.channel(), new EmptyHttpRequest(lastRequest)); - response.subscribe(subscriber); - publisher.subscribe(response); - } - } - - @Override - protected void bodyRequested(ChannelHandlerContext ctx) { - if (continueExpected) { - if (inFlight == 1) { - ctx.writeAndFlush(new DefaultFullHttpResponse(lastRequest.protocolVersion(), HttpResponseStatus.CONTINUE)); - continueExpected = false; - } else { - sendContinue = true; - } - } - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - super.handlerRemoved(ctx); - for (ChannelHandler dependent: dependentHandlers) { - try { - ctx.pipeline().remove(dependent); - } catch (NoSuchElementException e) { - // Ignore, maybe something else removed it - } - } - } -} diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/StreamedHttpMessage.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/StreamedHttpMessage.java deleted file mode 100644 index 4e23053..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/StreamedHttpMessage.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpMessage; -import org.reactivestreams.Publisher; - -/** - * Combines {@link HttpMessage} and {@link Publisher} into one - * message. So it represents an http message with a stream of {@link HttpContent} - * messages that can be subscribed to. - * - * Note that receivers of this message must consume the publisher, - * since the publisher will exert back pressure up the stream if not consumed. - */ -public interface StreamedHttpMessage extends HttpMessage, Publisher { -} diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/StreamedHttpRequest.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/StreamedHttpRequest.java deleted file mode 100644 index 67493be..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/StreamedHttpRequest.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import io.netty.handler.codec.http.HttpRequest; - -/** - * Combines {@link HttpRequest} and {@link StreamedHttpMessage} into one - * message. So it represents an http request with a stream of - * {@link io.netty.handler.codec.http.HttpContent} messages that can be subscribed to. - */ -public interface StreamedHttpRequest extends HttpRequest, StreamedHttpMessage { -} diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/StreamedHttpResponse.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/StreamedHttpResponse.java deleted file mode 100644 index a2050e9..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/StreamedHttpResponse.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import io.netty.handler.codec.http.HttpResponse; - -/** - * Combines {@link HttpResponse} and {@link StreamedHttpMessage} into one - * message. So it represents an http response with a stream of - * {@link io.netty.handler.codec.http.HttpContent} messages that can be subscribed to. - */ -public interface StreamedHttpResponse extends HttpResponse, StreamedHttpMessage { -} diff --git a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/WebSocketHttpResponse.java b/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/WebSocketHttpResponse.java deleted file mode 100644 index fcd6f0a..0000000 --- a/netty-http-server-reactive/src/main/java/org/xbib/netty/http/server/reactive/WebSocketHttpResponse.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.xbib.netty.http.server.reactive; - -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory; -import org.reactivestreams.Processor; - - -/** - * Combines {@link HttpResponse} and {@link Processor} - * into one message. So it represents an http response with a processor that can handle - * a WebSocket. - * - * This is only used for server side responses. For client side websocket requests, it's - * better to configure the reactive streams pipeline directly. - */ -public interface WebSocketHttpResponse extends HttpResponse, Processor { - /** - * Get the handshaker factory to use to reconfigure the channel. - * - * @return The handshaker factory. - */ - WebSocketServerHandshakerFactory handshakerFactory(); -} diff --git a/netty-http-server-reactive/src/test/java/org/xbib/netty/http/server/reactive/test/BatchedProducer.java b/netty-http-server-reactive/src/test/java/org/xbib/netty/http/server/reactive/test/BatchedProducer.java deleted file mode 100644 index adb3c72..0000000 --- a/netty-http-server-reactive/src/test/java/org/xbib/netty/http/server/reactive/test/BatchedProducer.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.xbib.netty.http.server.reactive.test; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundHandlerAdapter; -import io.netty.channel.ChannelPromise; - -import java.util.concurrent.atomic.AtomicLong; - -/** - * A batched producer. - * - * Responds to read requests with batches of elements according to batch size. When eofOn is reached, it closes the - * channel. - */ -public class BatchedProducer extends ChannelOutboundHandlerAdapter { - - protected final long eofOn; - protected final int batchSize; - protected final AtomicLong sequence; - - public BatchedProducer(long eofOn, int batchSize, long sequence) { - this.eofOn = eofOn; - this.batchSize = batchSize; - this.sequence = new AtomicLong(sequence); - } - - private boolean cancelled = false; - - - @Override - public void read(final ChannelHandlerContext ctx) throws Exception { - if (cancelled) { - throw new IllegalStateException("Received demand after being cancelled"); - } - ctx.pipeline().channel().eventLoop().parent().execute(new Runnable() { - @Override - public void run() { - for (int i = 0; i < batchSize && sequence.get() != eofOn; i++) { - ctx.fireChannelRead(sequence.getAndIncrement()); - } - if (eofOn == sequence.get()) { - ctx.fireChannelInactive(); - } else { - ctx.fireChannelReadComplete(); - } - } - }); - } - - @Override - public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - if (cancelled) { - throw new IllegalStateException("Cancelled twice"); - } - cancelled = true; - } -} diff --git a/netty-http-server-reactive/src/test/java/org/xbib/netty/http/server/reactive/test/ChannelPublisherTest.java b/netty-http-server-reactive/src/test/java/org/xbib/netty/http/server/reactive/test/ChannelPublisherTest.java deleted file mode 100644 index ddad237..0000000 --- a/netty-http-server-reactive/src/test/java/org/xbib/netty/http/server/reactive/test/ChannelPublisherTest.java +++ /dev/null @@ -1,157 +0,0 @@ -package org.xbib.netty.http.server.reactive.test; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoop; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.util.concurrent.DefaultPromise; -import io.netty.util.concurrent.Promise; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; -import org.xbib.netty.http.server.reactive.HandlerPublisher; - -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; - -class ChannelPublisherTest { - - private EventLoopGroup group; - private Channel channel; - private Publisher publisher; - private SubscriberProbe subscriber; - - @BeforeEach - void start() throws Exception { - group = new NioEventLoopGroup(); - EventLoop eventLoop = group.next(); - HandlerPublisher handlerPublisher = new HandlerPublisher<>(eventLoop, Channel.class); - Bootstrap bootstrap = new Bootstrap(); - bootstrap.channel(NioServerSocketChannel.class) - .group(eventLoop) - .option(ChannelOption.AUTO_READ, false) - .handler(handlerPublisher) - .localAddress("127.0.0.1", 0); - channel = bootstrap.bind().await().channel(); - this.publisher = handlerPublisher; - - subscriber = new SubscriberProbe<>(); - } - - @AfterEach - void stop() throws Exception { - channel.unsafe().closeForcibly(); - group.shutdownGracefully(); - } - - @Test - void test() throws Exception { - publisher.subscribe(subscriber); - Subscription sub = subscriber.takeSubscription(); - - // Try one cycle - sub.request(1); - Socket socket1 = connect(); - receiveConnection(); - readWriteData(socket1, 1); - - // Check back pressure - Socket socket2 = connect(); - subscriber.expectNoElements(); - - // Now request the next connection - sub.request(1); - receiveConnection(); - readWriteData(socket2, 2); - - // Close the channel - channel.close(); - subscriber.expectNoElements(); - subscriber.expectComplete(); - } - - private Socket connect() throws Exception { - InetSocketAddress address = (InetSocketAddress) channel.localAddress(); - return new Socket(address.getAddress(), address.getPort()); - } - - private void readWriteData(Socket socket, int data) throws Exception { - OutputStream os = socket.getOutputStream(); - os.write(data); - os.flush(); - InputStream is = socket.getInputStream(); - int received = is.read(); - socket.close(); - assertEquals(received, data); - } - - private void receiveConnection() throws Exception { - Channel channel = subscriber.take(); - channel.pipeline().addLast(new ChannelInboundHandlerAdapter() { - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - ctx.writeAndFlush(msg); - } - }); - group.register(channel); - } - - private class SubscriberProbe implements Subscriber { - final BlockingQueue subscriptions = new LinkedBlockingQueue<>(); - final BlockingQueue elements = new LinkedBlockingQueue<>(); - final Promise promise = new DefaultPromise<>(group.next()); - - public void onSubscribe(Subscription s) { - subscriptions.add(s); - } - - public void onNext(T t) { - elements.add(t); - } - - public void onError(Throwable t) { - promise.setFailure(t); - } - - public void onComplete() { - promise.setSuccess(null); - } - - Subscription takeSubscription() throws Exception { - Subscription sub = subscriptions.poll(100, TimeUnit.MILLISECONDS); - assertNotNull(sub); - return sub; - } - - T take() throws Exception { - T t = elements.poll(1000, TimeUnit.MILLISECONDS); - assertNotNull(t); - return t; - } - - void expectNoElements() throws Exception { - T t = elements.poll(100, TimeUnit.MILLISECONDS); - assertNull(t); - } - - void expectComplete() throws Exception { - promise.get(100, TimeUnit.MILLISECONDS); - } - } -} diff --git a/netty-http-server-reactive/src/test/java/org/xbib/netty/http/server/reactive/test/ClosedLoopChannel.java b/netty-http-server-reactive/src/test/java/org/xbib/netty/http/server/reactive/test/ClosedLoopChannel.java deleted file mode 100644 index 8d989c4..0000000 --- a/netty-http-server-reactive/src/test/java/org/xbib/netty/http/server/reactive/test/ClosedLoopChannel.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.xbib.netty.http.server.reactive.test; - -import io.netty.channel.AbstractChannel; -import io.netty.channel.ChannelConfig; -import io.netty.channel.ChannelMetadata; -import io.netty.channel.ChannelOutboundBuffer; -import io.netty.channel.ChannelPromise; -import io.netty.channel.DefaultChannelConfig; -import io.netty.channel.EventLoop; - -import java.net.SocketAddress; - -/** - * A closed loop channel that sends no events and receives no events, for testing purposes. - * - * Any outgoing events that reach the channel will throw an exception. All events should be caught - * be inserting a handler that catches them and responds accordingly. - */ -public class ClosedLoopChannel extends AbstractChannel { - - private final ChannelConfig config = new DefaultChannelConfig(this); - private static final ChannelMetadata metadata = new ChannelMetadata(false); - - private volatile boolean open = true; - private volatile boolean active = true; - - public ClosedLoopChannel() { - super(null); - } - - public void setOpen(boolean open) { - this.open = open; - } - - public void setActive(boolean active) { - this.active = active; - } - - @Override - protected AbstractUnsafe newUnsafe() { - return new AbstractUnsafe() { - @Override - public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { - throw new UnsupportedOperationException(); - } - }; - } - - @Override - protected boolean isCompatible(EventLoop loop) { - return true; - } - - @Override - protected SocketAddress localAddress0() { - throw new UnsupportedOperationException(); - } - - @Override - protected SocketAddress remoteAddress0() { - throw new UnsupportedOperationException(); - } - - @Override - protected void doBind(SocketAddress localAddress) throws Exception { - throw new UnsupportedOperationException(); - } - - @Override - protected void doDisconnect() throws Exception { - throw new UnsupportedOperationException(); - } - - @Override - protected void doClose() throws Exception { - this.open = false; - } - - @Override - protected void doBeginRead() throws Exception { - throw new UnsupportedOperationException(); - } - - @Override - protected void doWrite(ChannelOutboundBuffer in) throws Exception { - throw new UnsupportedOperationException(); - } - - @Override - public ChannelConfig config() { - return config; - } - - @Override - public boolean isOpen() { - return open; - } - - @Override - public boolean isActive() { - return active; - } - - @Override - public ChannelMetadata metadata() { - return metadata; - } -} diff --git a/settings.gradle b/settings.gradle index e8a0fe4..f64e994 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,6 +7,4 @@ include 'netty-http-client' include 'netty-http-client-rest' include 'netty-http-server-api' include 'netty-http-server' -include 'netty-http-server-reactive' include 'netty-http-server-rest' -include 'netty-http-rx'