package org.cloudfoundry.identity.uaa.util;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpsServer;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.routing.HttpRoutePlanner;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.HttpContext;
import org.cloudfoundry.identity.uaa.test.network.NetworkTestUtils;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLHandshakeException;
import java.io.File;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import static org.cloudfoundry.identity.uaa.util.UaaHttpRequestUtils.createRequestFactory;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.springframework.http.HttpStatus.OK;
public class UaaHttpRequestUtilsTest {
private static final String HTTP_HOST_PROPERTY = "http.proxyHost";
private static final String HTTP_PORT_PROPERTY = "http.proxyPort";
private static final String HTTPS_HOST_PROPERTY = "https.proxyHost";
private static final String HTTPS_PORT_PROPERTY = "https.proxyPort";
private static Map<String,String> systemProxyConfig = new HashMap<>();
private NetworkTestUtils.SimpleHttpResponseHandler httpResponseHandler;
private NetworkTestUtils.SimpleHttpResponseHandler httpsResponseHandler;
@BeforeClass
public static void storeSystemProxyConfig() {
for (String s : Arrays.asList(HTTP_HOST_PROPERTY, HTTP_PORT_PROPERTY, HTTPS_HOST_PROPERTY, HTTPS_PORT_PROPERTY)) {
systemProxyConfig.put(s, System.getProperty(s));
}
}
@AfterClass
public static void restoreSystemProxyConfig() {
for (Map.Entry<String,String> entry : systemProxyConfig.entrySet()) {
if (entry.getValue()!=null) {
System.setProperty(entry.getKey(), entry.getValue());
}
}
}
public void clearSystemProxyConfig() {
System.clearProperty(HTTPS_HOST_PROPERTY);
System.clearProperty(HTTPS_PORT_PROPERTY);
System.clearProperty(HTTP_HOST_PROPERTY);
System.clearProperty(HTTP_PORT_PROPERTY);
}
HttpsServer httpsServer;
HttpServer httpServer;
int sslPort;
int port;
private String httpsUrl;
private String httpUrl;
@Before
public void setup() throws Exception {
clearSystemProxyConfig();
File keystore = NetworkTestUtils.getKeystore(new Date(), 10);
sslPort = 23438;
port = sslPort+1;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
httpResponseHandler = new NetworkTestUtils.SimpleHttpResponseHandler(200, headers, "OK");
httpsResponseHandler = new NetworkTestUtils.SimpleHttpResponseHandler(200, headers, "OK");
httpsServer = NetworkTestUtils.startHttpsServer(sslPort, keystore, NetworkTestUtils.keyPass, httpsResponseHandler);
httpServer = NetworkTestUtils.startHttpServer(port, httpResponseHandler);
httpsUrl = "https://localhost:" + sslPort + "/";
httpUrl = "http://localhost:" + port + "/";
}
@After
public void teardown() throws Exception {
httpsServer.stop(0);
httpServer.stop(0);
}
@Test
public void testHttpProxy() throws Exception {
String host = "localhost";
System.setProperty(HTTP_HOST_PROPERTY, host);
System.setProperty(HTTP_PORT_PROPERTY, String.valueOf(port));
testHttpProxy("http://google.com:80/", port, host, true);
}
@Test
public void testHttpsProxy() throws Exception {
String host = "localhost";
System.setProperty(HTTPS_HOST_PROPERTY, host);
System.setProperty(HTTPS_PORT_PROPERTY, String.valueOf(port));
testHttpProxy("https://google.com:443/", port, host, false);
}
@Test
public void testHttpIpProxy() throws Exception {
String ip = "127.0.0.1";
System.setProperty(HTTP_HOST_PROPERTY, ip);
System.setProperty(HTTP_PORT_PROPERTY, String.valueOf(port));
testHttpProxy("http://google.com:80/", port, ip, true);
}
@Test
public void testHttpsIpProxy() throws Exception {
String ip = "127.0.0.1";
System.setProperty(HTTPS_HOST_PROPERTY, ip);
System.setProperty(HTTPS_PORT_PROPERTY, String.valueOf(port));
testHttpProxy("https://google.com:443/", port, ip, false);
}
public void testHttpProxy(String url, int expectedPort, String expectedHost, boolean wantHandlerInvoked) throws Exception {
HttpClientBuilder builder = UaaHttpRequestUtils.getClientBuilder(true);
HttpRoutePlanner planner = (HttpRoutePlanner) ReflectionTestUtils.getField(builder.build(), "routePlanner");
SystemProxyRoutePlanner routePlanner = new SystemProxyRoutePlanner(planner);
builder.setRoutePlanner(routePlanner);
RestTemplate template = new RestTemplate(UaaHttpRequestUtils.createRequestFactory(builder));
try {
template.getForObject(url,String.class);
} catch (Exception e) {
}
assertEquals(1, routePlanner.routes.size());
assertEquals(expectedHost, routePlanner.routes.get(0).getProxyHost().getHostName());
assertEquals(expectedPort, routePlanner.routes.get(0).getProxyHost().getPort());
assertEquals(wantHandlerInvoked, httpResponseHandler.wasInvoked());
}
@Test
public void skipSslValidation() {
RestTemplate restTemplate = new RestTemplate(createRequestFactory(true));
assertEquals(OK, restTemplate.getForEntity(httpsUrl, String.class).getStatusCode());
restTemplate.setRequestFactory(UaaHttpRequestUtils.createRequestFactory(true));
assertEquals(OK, restTemplate.getForEntity(httpsUrl, String.class).getStatusCode());
}
@Test
public void trustedOnly() {
RestTemplate restTemplate = new RestTemplate(UaaHttpRequestUtils.createRequestFactory(false));
try {
restTemplate.getForEntity(httpsUrl, String.class);
fail("We should not reach this step if the above URL is using a self signed certificate");
} catch (RestClientException e) {
assertEquals(SSLHandshakeException.class, e.getCause().getClass());
}
}
public static class SystemProxyRoutePlanner implements HttpRoutePlanner {
private final HttpRoutePlanner delegate;
public List<HttpRoute> routes = new LinkedList<>();
public SystemProxyRoutePlanner(HttpRoutePlanner delegate) {
this.delegate = delegate;
}
@Override
public HttpRoute determineRoute(HttpHost target, HttpRequest request, HttpContext context) throws HttpException {
HttpRoute route = delegate.determineRoute(target, request, context);
routes.add(route);
return route;
}
}
}