/*
* 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<HttpClientResponse<ByteBuf>> 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<HttpClientResponse<ByteBuf>> 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<HttpClientResponse<ByteBuf>> 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<HttpClientResponse<ByteBuf>> 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<HttpClientResponse<ByteBuf>> subscriber = sendRequest(requestUri);
assertRequestWritten(requestUri);
sendRedirects("http://localhost:8888/blah");
subscriber.awaitTerminalEvent();
subscriber.assertNoErrors();
assertThat("Unexpected onNext notifications count.", subscriber.getOnNextEvents(), hasSize(1));
HttpClientResponse<ByteBuf> 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<ByteBuf, ByteBuf> client = clientRule.getHttpClient().followRedirects(1);
TestSubscriber<HttpClientResponse<ByteBuf>> 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<ByteBuf, ByteBuf> pConfig = new PoolConfig<ByteBuf, ByteBuf>().maxConnections(10);
clientRule.setupPooledConnectionFactory(pConfig); // sets the client et al.
HttpClient<ByteBuf, ByteBuf> client = clientRule.getHttpClient().followRedirects(1);
final String requestUri = "/";
TestSubscriber<HttpClientResponse<ByteBuf>> 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<ByteBuf, ByteBuf> client = clientRule.getHttpClient().followRedirects(false);
final String requestUri = "/";
TestSubscriber<HttpClientResponse<ByteBuf>> subscriber = sendRequest(client, requestUri);
assertRequestWritten(requestUri);
sendRedirects("/blah2");
subscriber.awaitTerminalEvent();
subscriber.assertNoErrors();
assertThat("Unexpected onNext notifications count.", subscriber.getOnNextEvents(), hasSize(1));
HttpClientResponse<ByteBuf> 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<HttpClientResponse<ByteBuf>> 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<ByteBuf> 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<HttpClientResponse<ByteBuf>> 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<ByteBuf> 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<HttpClientResponse<ByteBuf>> sendRequest(HttpClient<ByteBuf, ByteBuf> client,
HttpMethod method, String uri) {
final HttpClientRequest<ByteBuf, ByteBuf> req = client.createRequest(method, uri);
TestSubscriber<HttpClientResponse<ByteBuf>> subscriber = new TestSubscriber<>();
req.subscribe(subscriber);
subscriber.assertNoErrors();
return subscriber;
}
private void assertRequestWritten(String uri) {
clientRule.assertRequestHeadersWritten(HttpMethod.GET, uri);
}
private static TestSubscriber<HttpClientResponse<ByteBuf>> sendRequest(HttpClient<ByteBuf, ByteBuf> client,
String uri) {
return sendRequest(client, HttpMethod.GET, uri);
}
private TestSubscriber<HttpClientResponse<ByteBuf>> sendRequest(String uri) {
return sendRequest(clientRule.getHttpClient().followRedirects(1), uri);
}
private TestSubscriber<HttpClientResponse<ByteBuf>> 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<EmbeddedChannelWithFeeder> 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);
}
}