-
Notifications
You must be signed in to change notification settings - Fork 184
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
API to support start/stop accepting connections
Motivation: Expose a way a user can yield accepting connection on server side, and resume on demand.? Modifications: ServerContext now supports an additional `acceptConnections(bool)` API that can be used to hint to the server the need for start/stop accepting. Result: More ways to control the service on-demand.
- Loading branch information
Showing
9 changed files
with
190 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
130 changes: 130 additions & 0 deletions
130
...netty/src/test/java/io/servicetalk/http/netty/ConnectionAcceptingNettyHttpServerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/* | ||
* Copyright © 2021 Apple Inc. and the ServiceTalk project authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.servicetalk.http.netty; | ||
|
||
import io.servicetalk.client.api.ConnectTimeoutException; | ||
import io.servicetalk.http.api.DefaultStreamingHttpRequestResponseFactory; | ||
import io.servicetalk.http.api.HttpServerBuilder; | ||
import io.servicetalk.http.api.StreamingHttpClient; | ||
import io.servicetalk.http.api.StreamingHttpRequest; | ||
import io.servicetalk.http.api.StreamingHttpRequestResponseFactory; | ||
import io.servicetalk.http.api.StreamingHttpResponse; | ||
import io.servicetalk.transport.api.ServiceTalkSocketOptions; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
import java.util.concurrent.ExecutionException; | ||
import java.util.concurrent.TimeoutException; | ||
|
||
import static io.servicetalk.buffer.netty.BufferAllocators.DEFAULT_ALLOCATOR; | ||
import static io.servicetalk.client.api.AutoRetryStrategyProvider.DISABLE_AUTO_RETRIES; | ||
import static io.servicetalk.client.api.LimitingConnectionFactoryFilter.withMax; | ||
import static io.servicetalk.concurrent.api.BlockingTestUtils.await; | ||
import static io.servicetalk.concurrent.api.BlockingTestUtils.awaitIndefinitely; | ||
import static io.servicetalk.http.api.DefaultHttpHeadersFactory.INSTANCE; | ||
import static io.servicetalk.http.api.HttpProtocolVersion.HTTP_1_1; | ||
import static io.servicetalk.http.api.HttpRequestMethod.GET; | ||
import static io.servicetalk.http.api.HttpResponseStatus.OK; | ||
import static io.servicetalk.http.netty.AbstractNettyHttpServerTest.ExecutorSupplier.CACHED; | ||
import static io.servicetalk.http.netty.TestServiceStreaming.SVC_ECHO; | ||
import static io.servicetalk.logging.api.LogLevel.TRACE; | ||
import static io.servicetalk.transport.api.ServiceTalkSocketOptions.CONNECT_TIMEOUT; | ||
import static java.lang.Boolean.TRUE; | ||
import static java.time.Duration.ofSeconds; | ||
import static java.util.concurrent.TimeUnit.SECONDS; | ||
import static org.hamcrest.MatcherAssert.assertThat; | ||
import static org.hamcrest.Matchers.instanceOf; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
|
||
class ConnectionAcceptingNettyHttpServerTest extends AbstractNettyHttpServerTest { | ||
|
||
static { | ||
System.setProperty("servicetalk.logger.wireLogLevel", "TRACE"); | ||
} | ||
|
||
private static final int TCP_BACKLOG = 1; | ||
private static final int CONNECT_TIMEOUT_MILLIS = (int) ofSeconds(5).toMillis(); | ||
private static final int VERIFY_REQUEST_AWAIT_SECS = 5; | ||
private static final int TRY_REQUEST_AWAIT_SECS = 1; | ||
|
||
private final StreamingHttpRequestResponseFactory reqRespFactory = | ||
new DefaultStreamingHttpRequestResponseFactory(DEFAULT_ALLOCATOR, INSTANCE, HTTP_1_1); | ||
|
||
@Override | ||
protected void configureServerBuilder(final HttpServerBuilder serverBuilder) { | ||
serverBuilder.listenSocketOption(ServiceTalkSocketOptions.SO_BACKLOG, TCP_BACKLOG); | ||
} | ||
|
||
@Test | ||
void testStopAcceptingAndResume() throws Exception { | ||
setUp(CACHED, CACHED); | ||
final StreamingHttpRequest request = reqRespFactory.newRequest(GET, SVC_ECHO); | ||
|
||
verifyNewConnectionRequest(request); | ||
|
||
serverContext().acceptConnections(false); | ||
// Netty will evaluate the auto-read on the next round, so the next connection will go through. | ||
verifyNewConnectionRequest(request); | ||
// This connection should get established but not accepted. | ||
tryNewConnectionRequest(request); | ||
|
||
// Restoring auto-read will resume accepting. | ||
serverContext().acceptConnections(true); | ||
verifyNewConnectionRequest(request); | ||
} | ||
|
||
@Test | ||
void testIdleTimeout() throws Exception { | ||
setUp(CACHED, CACHED); | ||
final StreamingHttpRequest request = reqRespFactory.newRequest(GET, SVC_ECHO); | ||
verifyNewConnectionRequest(request); | ||
|
||
serverContext().acceptConnections(false); | ||
// Netty will evaluate the auto-read on the next round, so the next connection will go through. | ||
verifyNewConnectionRequest(request); | ||
// Connection will establish but remain in the accept-queue | ||
// (i.e., NOT accepted by the server => occupying 1 backlog entry) | ||
tryNewConnectionRequest(request); | ||
final StreamingHttpClient httpClient2 = newClient(); | ||
// Since we control the backlog size, this connection won't establish (i.e., NO syn-ack) | ||
// timeout operator can be used to kill it or socket connection-timeout | ||
final ExecutionException executionException = | ||
assertThrows(ExecutionException.class, () -> awaitIndefinitely(httpClient2.request(request))); | ||
assertThat(executionException.getCause(), instanceOf(ConnectTimeoutException.class)); | ||
} | ||
|
||
private void tryNewConnectionRequest(final StreamingHttpRequest request) { | ||
final StreamingHttpClient httpClient = newClient(); | ||
assertThrows(TimeoutException.class, () -> await(httpClient.request(request), TRY_REQUEST_AWAIT_SECS, SECONDS)); | ||
} | ||
|
||
private StreamingHttpClient newClient() { | ||
return newClientBuilder() | ||
.appendConnectionFactoryFilter(withMax(1)) | ||
.autoRetryStrategy(DISABLE_AUTO_RETRIES) | ||
.enableWireLogging("servicetalk-tests-wire-logger", TRACE, TRUE::booleanValue) | ||
// It's important to use CONNECT_TIMEOUT here to verify that connections aren't establishing. | ||
.socketOption(CONNECT_TIMEOUT, CONNECT_TIMEOUT_MILLIS) | ||
.buildStreaming(); | ||
} | ||
|
||
private void verifyNewConnectionRequest(final StreamingHttpRequest request) throws Exception { | ||
final StreamingHttpClient httpClient = newClientBuilder().buildStreaming(); | ||
final StreamingHttpResponse response = await(httpClient.request(request), VERIFY_REQUEST_AWAIT_SECS, SECONDS); | ||
assert response != null; | ||
assertResponse(response, HTTP_1_1, OK, ""); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters