|
41 | 41 | import static org.mockito.Mockito.times;
|
42 | 42 | import static org.mockito.Mockito.when;
|
43 | 43 |
|
| 44 | +import com.github.tomakehurst.wiremock.http.Fault; |
44 | 45 | import com.github.tomakehurst.wiremock.http.trafficlistener.WiremockNetworkTrafficListener;
|
45 | 46 | import com.github.tomakehurst.wiremock.junit.WireMockRule;
|
| 47 | +import io.netty.channel.Channel; |
46 | 48 | import io.netty.channel.ChannelFactory;
|
47 | 49 | import io.netty.channel.ChannelFuture;
|
48 | 50 | import io.netty.channel.EventLoopGroup;
|
49 | 51 | import io.netty.channel.nio.NioEventLoopGroup;
|
50 | 52 | import io.netty.channel.pool.ChannelPool;
|
51 | 53 | import io.netty.channel.socket.nio.NioSocketChannel;
|
| 54 | +import io.netty.util.AttributeKey; |
52 | 55 | import java.io.IOException;
|
53 | 56 | import java.net.Socket;
|
54 | 57 | import java.net.URI;
|
@@ -225,6 +228,110 @@ protected ChannelPool newPool(URI key) {
|
225 | 228 | Mockito.verify(channelPool).close();
|
226 | 229 | }
|
227 | 230 |
|
| 231 | + @Test |
| 232 | + public void responseConnectionReused_shouldReleaseChannel() throws Exception { |
| 233 | + |
| 234 | + ChannelFactory channelFactory = mock(ChannelFactory.class); |
| 235 | + EventLoopGroup customEventLoopGroup = new NioEventLoopGroup(1); |
| 236 | + NioSocketChannel channel = new NioSocketChannel(); |
| 237 | + |
| 238 | + when(channelFactory.newChannel()).thenAnswer((Answer<NioSocketChannel>) invocationOnMock -> channel); |
| 239 | + SdkEventLoopGroup eventLoopGroup = SdkEventLoopGroup.create(customEventLoopGroup, channelFactory); |
| 240 | + |
| 241 | + NettyNioAsyncHttpClient customClient = |
| 242 | + (NettyNioAsyncHttpClient) NettyNioAsyncHttpClient.builder() |
| 243 | + .eventLoopGroup(eventLoopGroup) |
| 244 | + .maxConcurrency(1) |
| 245 | + .build(); |
| 246 | + |
| 247 | + makeSimpleRequest(customClient); |
| 248 | + verifyChannelRelease(channel); |
| 249 | + assertThat(channel.isShutdown()).isFalse(); |
| 250 | + |
| 251 | + customClient.close(); |
| 252 | + eventLoopGroup.eventLoopGroup().shutdownGracefully().awaitUninterruptibly(); |
| 253 | + } |
| 254 | + |
| 255 | + @Test |
| 256 | + public void connectionInactive_shouldReleaseChannel() throws Exception { |
| 257 | + |
| 258 | + ChannelFactory channelFactory = mock(ChannelFactory.class); |
| 259 | + EventLoopGroup customEventLoopGroup = new NioEventLoopGroup(1); |
| 260 | + NioSocketChannel channel = new NioSocketChannel(); |
| 261 | + |
| 262 | + when(channelFactory.newChannel()).thenAnswer((Answer<NioSocketChannel>) invocationOnMock -> channel); |
| 263 | + SdkEventLoopGroup eventLoopGroup = SdkEventLoopGroup.create(customEventLoopGroup, channelFactory); |
| 264 | + |
| 265 | + NettyNioAsyncHttpClient customClient = |
| 266 | + (NettyNioAsyncHttpClient) NettyNioAsyncHttpClient.builder() |
| 267 | + .eventLoopGroup(eventLoopGroup) |
| 268 | + .maxConcurrency(1) |
| 269 | + .build(); |
| 270 | + |
| 271 | + |
| 272 | + String body = randomAlphabetic(10); |
| 273 | + URI uri = URI.create("http://localhost:" + mockServer.port()); |
| 274 | + SdkHttpRequest request = createRequest(uri); |
| 275 | + RecordingResponseHandler recorder = new RecordingResponseHandler(); |
| 276 | + |
| 277 | + |
| 278 | + stubFor(any(urlPathEqualTo("/")).willReturn(aResponse().withBody(body) |
| 279 | + .withStatus(500) |
| 280 | + .withFault(Fault.RANDOM_DATA_THEN_CLOSE))); |
| 281 | + |
| 282 | + customClient.execute(AsyncExecuteRequest.builder() |
| 283 | + .request(request) |
| 284 | + .requestContentPublisher(createProvider("")) |
| 285 | + .responseHandler(recorder).build()); |
| 286 | + |
| 287 | + verifyChannelRelease(channel); |
| 288 | + assertThat(channel.isShutdown()).isTrue(); |
| 289 | + |
| 290 | + customClient.close(); |
| 291 | + eventLoopGroup.eventLoopGroup().shutdownGracefully().awaitUninterruptibly(); |
| 292 | + } |
| 293 | + |
| 294 | + @Test |
| 295 | + public void responseConnectionClosed_shouldCloseAndReleaseChannel() throws Exception { |
| 296 | + |
| 297 | + ChannelFactory channelFactory = mock(ChannelFactory.class); |
| 298 | + EventLoopGroup customEventLoopGroup = new NioEventLoopGroup(1); |
| 299 | + NioSocketChannel channel = new NioSocketChannel(); |
| 300 | + |
| 301 | + when(channelFactory.newChannel()).thenAnswer((Answer<NioSocketChannel>) invocationOnMock -> channel); |
| 302 | + |
| 303 | + URI uri = URI.create("http://localhost:" + mockServer.port()); |
| 304 | + SdkHttpRequest request = createRequest(uri); |
| 305 | + RecordingResponseHandler recorder = new RecordingResponseHandler(); |
| 306 | + |
| 307 | + SdkEventLoopGroup eventLoopGroup = SdkEventLoopGroup.create(customEventLoopGroup, channelFactory); |
| 308 | + |
| 309 | + NettyNioAsyncHttpClient customClient = |
| 310 | + (NettyNioAsyncHttpClient) NettyNioAsyncHttpClient.builder() |
| 311 | + .eventLoopGroup(eventLoopGroup) |
| 312 | + .maxConcurrency(1) |
| 313 | + .build(); |
| 314 | + |
| 315 | + String body = randomAlphabetic(10); |
| 316 | + |
| 317 | + stubFor(any(urlPathEqualTo("/")).willReturn(aResponse().withBody(body) |
| 318 | + .withStatus(500) |
| 319 | + .withHeader("Connection", "close") |
| 320 | + )); |
| 321 | + |
| 322 | + customClient.execute(AsyncExecuteRequest.builder() |
| 323 | + .request(request) |
| 324 | + .requestContentPublisher(createProvider("")) |
| 325 | + .responseHandler(recorder).build()); |
| 326 | + recorder.completeFuture.get(5, TimeUnit.SECONDS); |
| 327 | + |
| 328 | + verifyChannelRelease(channel); |
| 329 | + assertThat(channel.isShutdown()).isTrue(); |
| 330 | + |
| 331 | + customClient.close(); |
| 332 | + eventLoopGroup.eventLoopGroup().shutdownGracefully().awaitUninterruptibly(); |
| 333 | + } |
| 334 | + |
228 | 335 | /**
|
229 | 336 | * Make a simple async request and wait for it to fiish.
|
230 | 337 | *
|
@@ -491,6 +598,11 @@ public void testExceptionMessageChanged_WhenConnectionTimeoutErrorEncountered()
|
491 | 598 | customClient.close();
|
492 | 599 | }
|
493 | 600 |
|
| 601 | + private void verifyChannelRelease(Channel channel) throws InterruptedException { |
| 602 | + Thread.sleep(1000); |
| 603 | + assertThat(channel.attr(AttributeKey.valueOf("channelPool")).get()).isNull(); |
| 604 | + } |
| 605 | + |
494 | 606 | private RecordingResponseHandler makeSimpleRequestAndReturnResponseHandler(SdkAsyncHttpClient client) throws Exception {
|
495 | 607 | String body = randomAlphabetic(10);
|
496 | 608 | URI uri = URI.create("http://localhost:" + mockServer.port());
|
|
0 commit comments