Skip to content

Commit

Permalink
RedirectingClientAndConnectionFilterTest should leverage AbstractHttp… (
Browse files Browse the repository at this point in the history
apple#867)

__Motivation__

`RedirectingClientAndConnectionFilterTest` could leverage the `AbstractHttpRequesterFilterTest` test tooling. Its approach is closely related to the one in `RedirectingClientAndConnectionFilterTest` and can reuse most of the code.

__Modifications__

- `RedirectingClientAndConnectionFilterTest` extends `AbstractHttpRequesterFilterTest`
- Add `StrategyInfluencerAwareConversionsTest` for static access to package-private factory methods

__Result__

Resolves (apple#396): RedirectingClientAndConnectionFilterTest should leverage AbstractHttpRequesterFilterTest
  • Loading branch information
volyx authored and Nitesh Kant committed Nov 21, 2019
1 parent 56e0dd7 commit 03a7b0a
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 153 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright © 2019 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.api;

import java.util.function.Predicate;

public final class ConditionalFilterFactory
implements StreamingHttpConnectionFilterFactory, StreamingHttpClientFilterFactory {
private final Predicate<StreamingHttpRequest> predicate;
private final FilterFactory predicateFactory;

public ConditionalFilterFactory(final Predicate<StreamingHttpRequest> predicate,
final FilterFactory predicateFactory) {
this.predicate = predicate;
this.predicateFactory = predicateFactory;
}

@Override
public StreamingHttpClientFilter create(final FilterableStreamingHttpClient client) {
return new ConditionalHttpClientFilter(predicate, predicateFactory.create(client), client);
}

@Override
public StreamingHttpConnectionFilter create(final FilterableStreamingHttpConnection connection) {
return new ConditionalHttpConnectionFilter(predicate, predicateFactory.create(connection), connection);
}

public FilterFactory append(FilterFactory append) {
StreamingHttpClientFilterFactory clientFactory = append((StreamingHttpClientFilterFactory) append);
StreamingHttpConnectionFilterFactory connectionFactory = append((StreamingHttpConnectionFilterFactory) append);
return new FilterFactory() {
@Override
public StreamingHttpClientFilter create(final FilterableStreamingHttpClient client) {
return clientFactory.create(client);
}

@Override
public StreamingHttpConnectionFilter create(final FilterableStreamingHttpConnection connection) {
return connectionFactory.create(connection);
}
};
}

public interface FilterFactory extends StreamingHttpClientFilterFactory, StreamingHttpConnectionFilterFactory {

static <FF extends StreamingHttpClientFilterFactory & StreamingHttpConnectionFilterFactory> FilterFactory from(
FF original) {
return new FilterFactory() {
@Override
public StreamingHttpClientFilter create(final FilterableStreamingHttpClient client) {
return original.create(client);
}

@Override
public StreamingHttpConnectionFilter create(final FilterableStreamingHttpConnection connection) {
return original.create(connection);
}
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,209 +15,149 @@
*/
package io.servicetalk.http.netty;

import io.servicetalk.concurrent.api.AsyncCloseables;
import io.servicetalk.concurrent.api.Completable;
import io.servicetalk.concurrent.api.CompositeCloseable;
import io.servicetalk.concurrent.api.Single;
import io.servicetalk.concurrent.internal.ServiceTalkTestTimeout;
import io.servicetalk.http.api.AbstractHttpRequesterFilterTest;
import io.servicetalk.http.api.BlockingHttpRequester;
import io.servicetalk.http.api.FilterableReservedStreamingHttpConnection;
import io.servicetalk.http.api.ConditionalFilterFactory;
import io.servicetalk.http.api.ConditionalFilterFactory.FilterFactory;
import io.servicetalk.http.api.HttpClient;
import io.servicetalk.http.api.HttpConnection;
import io.servicetalk.http.api.HttpExecutionStrategy;
import io.servicetalk.http.api.HttpRequest;
import io.servicetalk.http.api.HttpRequestMetaData;
import io.servicetalk.http.api.HttpResponse;
import io.servicetalk.http.api.ReservedStreamingHttpConnectionFilter;
import io.servicetalk.http.api.StreamingHttpClient;
import io.servicetalk.http.api.StreamingHttpClientFilter;
import io.servicetalk.http.utils.RedirectingHttpRequesterFilter;
import io.servicetalk.transport.api.HostAndPort;
import io.servicetalk.transport.api.ServerContext;

import org.junit.Rule;
import org.junit.Before;
import org.junit.Test;
import org.junit.rules.Timeout;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import java.net.InetSocketAddress;
import javax.net.ssl.SSLSession;

import static io.servicetalk.concurrent.api.Single.succeeded;
import static io.servicetalk.http.api.HttpExecutionStrategies.noOffloadsStrategy;
import static io.servicetalk.http.api.HttpHeaderNames.HOST;
import static io.servicetalk.http.api.HttpHeaderNames.LOCATION;
import static io.servicetalk.http.api.HttpProtocolVersion.HTTP_1_0;
import static io.servicetalk.http.api.HttpResponseStatus.OK;
import static io.servicetalk.http.api.HttpResponseStatus.PERMANENT_REDIRECT;
import static io.servicetalk.http.netty.RedirectingClientAndConnectionFilterTest.Type.Client;
import static io.servicetalk.http.netty.RedirectingClientAndConnectionFilterTest.Type.Reserved;
import static io.servicetalk.transport.netty.internal.AddressUtils.hostHeader;
import static io.servicetalk.transport.netty.internal.AddressUtils.localAddress;
import static java.lang.String.format;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;

/**
* This test-case is for integration testing the {@link RedirectingHttpRequesterFilter} with the various types
* of {@link HttpClient} and {@link HttpConnection} builders.
*/
@RunWith(Parameterized.class)
public final class RedirectingClientAndConnectionFilterTest {
public final class RedirectingClientAndConnectionFilterTest extends AbstractHttpRequesterFilterTest {

@Rule
public final Timeout timeout = new ServiceTalkTestTimeout();
private SSLSession session;

private final Type type;

protected enum Type { Client, Reserved }

public RedirectingClientAndConnectionFilterTest(Type type) {
this.type = type;
public RedirectingClientAndConnectionFilterTest(final RequesterType type, final SecurityType security) {
super(type, security);
}

@Parameters(name = "{0}")
public static Object[] getParameters() {
return new Object[]{Client, Reserved};
@Before
public void setUp() {
session = mock(SSLSession.class);
}

private BlockingHttpRequester newRequester(ServerContext serverContext) throws Exception {
final InetSocketAddress serverSocketAddress = (InetSocketAddress) serverContext.listenAddress();
final HostAndPort serverHostAndPort = HostAndPort.of(serverSocketAddress);
switch (type) {
case Client:
return HttpClients.forSingleAddress(serverHostAndPort)
.appendClientFilter(r -> r.headers().contains("X-REDIRECT"),
new RedirectingHttpRequesterFilter())
.buildBlocking();
case Reserved:
CompositeCloseable closeables = AsyncCloseables.newCompositeCloseable();
try {
StreamingHttpClient client = closeables.prepend(HttpClients.forSingleAddress(serverHostAndPort)
.appendClientFilter(clientFilter -> new StreamingHttpClientFilter(clientFilter) {
@Override
public Single<? extends FilterableReservedStreamingHttpConnection> reserveConnection(
final HttpExecutionStrategy strategy, final HttpRequestMetaData metaData) {
return delegate().reserveConnection(strategy, metaData).map(r ->
new ReservedStreamingHttpConnectionFilter(closeables.prepend(r)) {
@Override
public Completable closeAsync() {
return closeables.closeAsync();
}

@Override
public Completable closeAsyncGracefully() {
return closeables.closeAsyncGracefully();
}
}
);
}
})
.appendClientFilter(r -> r.headers().contains("X-REDIRECT"),
new RedirectingHttpRequesterFilter())
.buildStreaming());
return client.asBlockingClient().reserveConnection(client.get(""));
} catch (Throwable t) {
closeables.close();
throw t;
}
default:
throw new IllegalArgumentException(type.name());
}
@Override
protected SSLSession sslSession() {
return session;
}

@Test
public void redirectFilterNoHostHeaderRelativeLocation() throws Exception {
try (ServerContext serverContext = HttpServers.forAddress(localAddress(0))
.listenBlockingAndAwait((ctx, request, responseFactory) -> {
if (request.requestTarget().equals("/")) {
return responseFactory.permanentRedirect()
.addHeader(LOCATION, "/next");
}
return responseFactory.ok();
}); BlockingHttpRequester client = newRequester(serverContext)) {

HttpRequest request = client.get("/");
HttpResponse response = client.request(noOffloadsStrategy(), request);
assertThat(response.status(), equalTo(PERMANENT_REDIRECT));

response = client.request(noOffloadsStrategy(), request.addHeader("X-REDIRECT", "TRUE"));
assertThat(response.status(), equalTo(OK));

// HTTP/1.0 doesn't support HOST, ensure that we don't get any errors and fallback to redirect
response = client.request(noOffloadsStrategy(),
client.get("/")
.version(HTTP_1_0)
.addHeader("X-REDIRECT", "TRUE"));
assertThat(response.status(), equalTo(PERMANENT_REDIRECT));
}
BlockingHttpRequester client = asBlockingRequester(createFilter((responseFactory, request) -> {
if (request.requestTarget().equals("/")) {
return succeeded(responseFactory.permanentRedirect().addHeader(LOCATION, "/next"));
}
return succeeded(responseFactory.ok());

}, newFilterFactory()));

HttpRequest request = client.get("/");
HttpResponse response = client.request(noOffloadsStrategy(), request);
assertThat(response.status(), equalTo(PERMANENT_REDIRECT));

response = client.request(noOffloadsStrategy(), request.addHeader("X-REDIRECT", "TRUE"));
assertThat(response.status(), equalTo(OK));

// HTTP/1.0 doesn't support HOST, ensure that we don't get any errors and fallback to redirect
response = client.request(noOffloadsStrategy(),
client.get("/")
.version(HTTP_1_0)
.addHeader("X-REDIRECT", "TRUE"));
assertThat(response.status(), equalTo(PERMANENT_REDIRECT));
}

@Test
public void redirectFilterNoHostHeaderAbsoluteLocation() throws Exception {
try (ServerContext serverContext = HttpServers.forAddress(localAddress(0))
.listenBlockingAndAwait((ctx, request, responseFactory) -> {
if (request.requestTarget().equals("/")) {
InetSocketAddress socketAddress = (InetSocketAddress) ctx.localAddress();
return responseFactory.permanentRedirect().addHeader(LOCATION,
format("http://%s/next", hostHeader(HostAndPort.of(socketAddress))));
}
return responseFactory.ok();
}); BlockingHttpRequester client = newRequester(serverContext)) {

HttpRequest request = client.get("/");
HttpResponse response = client.request(noOffloadsStrategy(), request);
assertThat(response.status(), equalTo(PERMANENT_REDIRECT));

response = client.request(noOffloadsStrategy(), request.addHeader("X-REDIRECT", "TRUE"));
assertThat(response.status(), equalTo(OK));

// HTTP/1.0 doesn't support HOST, ensure that we don't get any errors and fallback to redirect
response = client.request(noOffloadsStrategy(),
client.get("/")
.version(HTTP_1_0)
.addHeader("X-REDIRECT", "TRUE"));
assertThat(response.status(), equalTo(PERMANENT_REDIRECT));
}
BlockingHttpRequester client = asBlockingRequester(createFilter((responseFactory, request) -> {
if (request.requestTarget().equals("/")) {
return succeeded(responseFactory.permanentRedirect().addHeader(LOCATION,
format("http://%s/next", hostHeader(HostAndPort.of(remoteAddress())))));
}
return succeeded(responseFactory.ok());
}, newFilterFactory()));
HttpRequest request = client.get("/");
HttpResponse response = client.request(noOffloadsStrategy(), request);
assertThat(response.status(), equalTo(PERMANENT_REDIRECT));

response = client.request(noOffloadsStrategy(), request.addHeader("X-REDIRECT", "TRUE"));
assertThat(response.status(), equalTo(OK));

// HTTP/1.0 doesn't support HOST, ensure that we don't get any errors and fallback to redirect
response = client.request(noOffloadsStrategy(),
client.get("/")
.version(HTTP_1_0)
.addHeader("X-REDIRECT", "TRUE"));
assertThat(response.status(), equalTo(PERMANENT_REDIRECT));
}

@Test
public void redirectFilterWithHostHeaderRelativeLocation() throws Exception {
try (ServerContext serverContext = HttpServers.forAddress(localAddress(0))
.listenBlockingAndAwait((ctx, request, responseFactory) -> {
if (request.requestTarget().equals("/")) {
return responseFactory.permanentRedirect()
.addHeader(LOCATION, "/next");
}
return responseFactory.ok();
}); BlockingHttpRequester client = newRequester(serverContext)) {

HttpRequest request = client.get("/").addHeader(HOST, "servicetalk.io");
HttpResponse response = client.request(noOffloadsStrategy(), request);
assertThat(response.status(), equalTo(PERMANENT_REDIRECT));

response = client.request(noOffloadsStrategy(), request.addHeader("X-REDIRECT", "TRUE"));
assertThat(response.status(), equalTo(OK));
}

BlockingHttpRequester client = asBlockingRequester(createFilter((responseFactory, request) -> {
if (request.requestTarget().equals("/")) {
return succeeded(responseFactory.permanentRedirect()
.addHeader(LOCATION, "/next"));
}
return succeeded(responseFactory.ok());
}, newFilterFactory()));
HttpRequest request = client.get("/").addHeader(HOST, "servicetalk.io");
HttpResponse response = client.request(noOffloadsStrategy(), request);
assertThat(response.status(), equalTo(PERMANENT_REDIRECT));

response = client.request(noOffloadsStrategy(), request.addHeader("X-REDIRECT", "TRUE"));
assertThat(response.status(), equalTo(OK));
}

@Test
public void redirectFilterWithHostHeaderAbsoluteLocation() throws Exception {
try (ServerContext serverContext = HttpServers.forAddress(localAddress(0))
.listenBlockingAndAwait((ctx, request, responseFactory) -> {
if (request.requestTarget().equals("/")) {
return responseFactory.permanentRedirect()
.addHeader(LOCATION, "http://servicetalk.io/next");
}
return responseFactory.ok();
}); BlockingHttpRequester client = newRequester(serverContext)) {

HttpRequest request = client.get("/").addHeader(HOST, "servicetalk.io");
HttpResponse response = client.request(noOffloadsStrategy(), request);
assertThat(response.status(), equalTo(PERMANENT_REDIRECT));

response = client.request(noOffloadsStrategy(), request.addHeader("X-REDIRECT", "TRUE"));
assertThat(response.status(), equalTo(OK));
}

BlockingHttpRequester client = asBlockingRequester(createFilter((responseFactory, request) -> {
if (request.requestTarget().equals("/")) {
return succeeded(responseFactory.permanentRedirect()
.addHeader(LOCATION, "http://servicetalk.io/next"));
}
return succeeded(responseFactory.ok());
}, newFilterFactory()));
HttpRequest request = client.get("/").addHeader(HOST, "servicetalk.io");
HttpResponse response = client.request(noOffloadsStrategy(), request);
assertThat(response.status(), equalTo(PERMANENT_REDIRECT));

response = client.request(noOffloadsStrategy(), request.addHeader("X-REDIRECT", "TRUE"));
assertThat(response.status(), equalTo(OK));
}

private FilterFactory newFilterFactory() {
return new ConditionalFilterFactory(request -> request.headers().contains("X-REDIRECT"),
FilterFactory.from(new RedirectingHttpRequesterFilter()))
.append(FilterFactory.from(
new HostHeaderHttpRequesterFilter(HostAndPort.of(remoteAddress()).toString())));
}
}

0 comments on commit 03a7b0a

Please sign in to comment.