Skip to content

Commit

Permalink
SimpleHttpClient: Use default HTTP/HTTPS ports (#188)
Browse files Browse the repository at this point in the history
* SimpleHttpClient: Send to default HTTP/HTTPS ports when a specific port is not set in an HTTP request.
  • Loading branch information
mikkokar authored Jun 14, 2018
1 parent fc6892b commit f2219f5
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
* A client that uses netty as transport.
*/
public final class SimpleHttpClient implements FullHttpClient {
private static final int DEFAULT_HTTPS_PORT = 443;
private static final int DEFAULT_HTTP_PORT = 80;

private final Optional<String> userAgent;
private final ConnectionSettings connectionSettings;
private final int maxResponseSize;
Expand Down Expand Up @@ -84,7 +87,13 @@ private static Origin originFromRequest(FullHttpRequest request) {
.orElseThrow(() -> new IllegalArgumentException("Cannot send request " + request + " as URL is not absolute and no HOST header is present"));
});

return newOriginBuilder(HostAndPort.fromString(hostAndPort)).build();
HostAndPort host = HostAndPort.fromString(hostAndPort);

if (host.getPortOrDefault(-1) < 0) {
host = host.withDefaultPort(request.isSecure() ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT);
}

return newOriginBuilder(host).build();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.hotels.styx.api.HttpRequest;
import com.hotels.styx.api.HttpResponse;
import com.hotels.styx.api.client.Connection;
import com.hotels.styx.api.client.Origin;
import com.hotels.styx.api.service.TlsSettings;
import org.mockito.ArgumentCaptor;
import org.testng.annotations.BeforeMethod;
Expand All @@ -36,6 +37,7 @@
import static com.hotels.styx.api.FullHttpRequest.get;
import static com.hotels.styx.api.HttpHeaderNames.HOST;
import static com.hotels.styx.api.HttpHeaderNames.USER_AGENT;
import static com.hotels.styx.api.HttpResponse.response;
import static com.hotels.styx.api.messages.HttpResponseStatus.OK;
import static com.hotels.styx.client.Protocol.HTTP;
import static com.hotels.styx.client.Protocol.HTTPS;
Expand All @@ -62,23 +64,23 @@ public void setUp() {
}

@Test
public void sendsHttp() throws IOException {
public void sendsHttp() {
withOrigin(HTTP, port -> {
FullHttpResponse response = await(httpClient().sendRequest(httpRequest(port)));
assertThat(response.status(), is(OK));
});
}

@Test
public void sendsHttps() throws IOException {
public void sendsHttps() {
withOrigin(HTTPS, port -> {
FullHttpResponse response = await(httpsClient().sendRequest(httpsRequest(port)));
assertThat(response.status(), is(OK));
});
}

@Test(expectedExceptions = Exception.class)
public void cannotSendHttpsWhenConfiguredForHttp() throws IOException {
public void cannotSendHttpsWhenConfiguredForHttp() {
withOrigin(HTTPS, port -> {
FullHttpResponse response = await(httpClient().sendRequest(httpsRequest(port)));
assertThat(response.status(), is(OK));
Expand All @@ -93,50 +95,10 @@ public void cannotSendHttpWhenConfiguredForHttps() throws IOException {
});
}

private SimpleHttpClient httpClient() {
return new SimpleHttpClient.Builder()
.build();
}

private SimpleHttpClient httpsClient() {
return new SimpleHttpClient.Builder()
.connectionSettings(new ConnectionSettings(1000))
.tlsSettings(new TlsSettings.Builder()
.authenticate(false)
.build())
.responseTimeoutMillis(6000)
.build();
}

private FullHttpRequest httpRequest(int port) {
return get("http://localhost:" + port)
.header(HOST, "localhost:" + port)
.build();
}

private FullHttpRequest httpsRequest(int port) {
return get("https://localhost:" + port)
.header(HOST, "localhost:" + port)
.build();
}

private void withOrigin(Protocol protocol, IntConsumer portConsumer) {
WireMockServer server = new WireMockServer(wireMockConfig().dynamicPort().dynamicHttpsPort());

try {
server.start();
int port = protocol == HTTP ? server.port() : server.httpsPort();
server.stubFor(WireMock.get(urlStartingWith("/")).willReturn(aResponse().withStatus(200)));
portConsumer.accept(port);
} finally {
server.stop();
}
}

@Test
public void willNotSetAnyUserAgentIfNotSpecified() {
Connection mockConnection = mock(Connection.class);
when(mockConnection.write(any(HttpRequest.class))).thenReturn(Observable.just(HttpResponse.response().build()));
when(mockConnection.write(any(HttpRequest.class))).thenReturn(Observable.just(response().build()));

SimpleHttpClient client = new SimpleHttpClient.Builder()
.setConnectionFactory((origin, connectionSettings) -> Observable.just(mockConnection))
Expand All @@ -152,7 +114,7 @@ public void willNotSetAnyUserAgentIfNotSpecified() {
@Test
public void setsTheSpecifiedUserAgentWhenSpecified() {
Connection mockConnection = mock(Connection.class);
when(mockConnection.write(any(HttpRequest.class))).thenReturn(Observable.just(HttpResponse.response().build()));
when(mockConnection.write(any(HttpRequest.class))).thenReturn(Observable.just(response().build()));

SimpleHttpClient client = new SimpleHttpClient.Builder()
.setConnectionFactory((origin, connectionSettings) -> Observable.just(mockConnection))
Expand All @@ -169,7 +131,7 @@ public void setsTheSpecifiedUserAgentWhenSpecified() {
@Test
public void retainsTheUserAgentStringFromTheRequest() {
Connection mockConnection = mock(Connection.class);
when(mockConnection.write(any(HttpRequest.class))).thenReturn(Observable.just(HttpResponse.response().build()));
when(mockConnection.write(any(HttpRequest.class))).thenReturn(Observable.just(response().build()));

SimpleHttpClient client = new SimpleHttpClient.Builder()
.setConnectionFactory((origin, connectionSettings) -> Observable.just(mockConnection))
Expand All @@ -194,4 +156,95 @@ public void requestWithNoHostOrUrlAuthorityCausesException() {

await(client.sendRequest(request));
}

@Test
public void sendsToDefaultHttpPort() {
Connection.Factory connectionFactory = mockConnectionFactory(mockConnection(response(OK).build()));

SimpleHttpClient client = new SimpleHttpClient.Builder()
.setConnectionFactory(connectionFactory)
.build();

client.sendRequest(get("/")
.header(HOST, "localhost")
.build());

ArgumentCaptor<Origin> originCaptor = ArgumentCaptor.forClass(Origin.class);
verify(connectionFactory).createConnection(originCaptor.capture(), any(Connection.Settings.class));

assertThat(originCaptor.getValue().host().getPort(), is(80));
}

@Test
public void sendsToDefaultHttpsPort() {
Connection.Factory connectionFactory = mockConnectionFactory(mockConnection(response(OK).build()));

SimpleHttpClient client = new SimpleHttpClient.Builder()
.setConnectionFactory(connectionFactory)
.build();

client.sendRequest(get("/")
.secure(true)
.header(HOST, "localhost")
.build());

ArgumentCaptor<Origin> originCaptor = ArgumentCaptor.forClass(Origin.class);
verify(connectionFactory).createConnection(originCaptor.capture(), any(Connection.Settings.class));

assertThat(originCaptor.getValue().host().getPort(), is(443));
}

private SimpleHttpClient httpClient() {
return new SimpleHttpClient.Builder()
.build();
}

private SimpleHttpClient httpsClient() {
return new SimpleHttpClient.Builder()
.connectionSettings(new ConnectionSettings(1000))
.tlsSettings(new TlsSettings.Builder()
.authenticate(false)
.build())
.responseTimeoutMillis(6000)
.build();
}

private FullHttpRequest httpRequest(int port) {
return get("http://localhost:" + port)
.header(HOST, "localhost:" + port)
.build();
}

private FullHttpRequest httpsRequest(int port) {
return get("https://localhost:" + port)
.header(HOST, "localhost:" + port)
.build();
}

private void withOrigin(Protocol protocol, IntConsumer portConsumer) {
WireMockServer server = new WireMockServer(wireMockConfig().dynamicPort().dynamicHttpsPort());

try {
server.start();
int port = protocol == HTTP ? server.port() : server.httpsPort();
server.stubFor(WireMock.get(urlStartingWith("/")).willReturn(aResponse().withStatus(200)));
portConsumer.accept(port);
} finally {
server.stop();
}
}

private Connection mockConnection(HttpResponse response) {
Connection connection = mock(Connection.class);
when(connection.write(any(HttpRequest.class))).thenReturn(Observable.just(response));
return connection;
}

private Connection.Factory mockConnectionFactory(Connection connection) {
Connection.Factory factory = mock(Connection.Factory.class);
when(factory.createConnection(any(Origin.class), any(Connection.Settings.class)))
.thenReturn(Observable.just(connection));
return factory;
}

}

0 comments on commit f2219f5

Please sign in to comment.