1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net.http; 18 19 import com.google.mockwebserver.MockResponse; 20 import com.google.mockwebserver.MockWebServer; 21 import com.google.mockwebserver.RecordedRequest; 22 import com.google.mockwebserver.SocketPolicy; 23 import java.io.IOException; 24 import java.io.InputStreamReader; 25 import java.io.Reader; 26 import java.io.StringWriter; 27 import java.util.List; 28 import junit.framework.TestCase; 29 import libcore.javax.net.ssl.TestSSLContext; 30 import org.apache.http.HttpHost; 31 import org.apache.http.HttpRequest; 32 import org.apache.http.HttpResponse; 33 import org.apache.http.client.HttpClient; 34 import org.apache.http.client.methods.HttpGet; 35 import org.apache.http.conn.params.ConnRoutePNames; 36 import org.apache.http.conn.params.ConnRouteParams; 37 import org.apache.http.conn.scheme.Scheme; 38 import org.apache.http.conn.ssl.AllowAllHostnameVerifier; 39 import org.apache.http.conn.ssl.SSLSocketFactory; 40 41 public abstract class AbstractProxyTest extends TestCase { 42 43 private MockWebServer server = new MockWebServer(); 44 45 protected abstract HttpClient newHttpClient(); 46 47 @Override protected void tearDown() throws Exception { 48 System.clearProperty("proxyHost"); 49 System.clearProperty("proxyPort"); 50 System.clearProperty("http.proxyHost"); 51 System.clearProperty("http.proxyPort"); 52 System.clearProperty("https.proxyHost"); 53 System.clearProperty("https.proxyPort"); 54 55 server.shutdown(); 56 super.tearDown(); 57 } 58 59 public void testConnectToHttps() throws Exception { 60 TestSSLContext testSSLContext = TestSSLContext.create(); 61 62 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 63 server.enqueue(new MockResponse() 64 .setResponseCode(200) 65 .setBody("this response comes via HTTPS")); 66 server.play(); 67 68 HttpClient httpClient = newHttpClient(); 69 70 SSLSocketFactory sslSocketFactory = newSslSocketFactory(testSSLContext); 71 sslSocketFactory.setHostnameVerifier(new AllowAllHostnameVerifier()); 72 httpClient.getConnectionManager().getSchemeRegistry() 73 .register(new Scheme("https", sslSocketFactory, server.getPort())); 74 75 HttpResponse response = httpClient.execute( 76 new HttpGet("https://localhost:" + server.getPort() + "/foo")); 77 assertEquals("this response comes via HTTPS", contentToString(response)); 78 79 RecordedRequest request = server.takeRequest(); 80 assertEquals("GET /foo HTTP/1.1", request.getRequestLine()); 81 } 82 83 private SSLSocketFactory newSslSocketFactory(TestSSLContext testSSLContext) throws Exception { 84 // call through to Apache HTTP's non-public SSLSocketFactory constructor 85 return SSLSocketFactory.class.getConstructor(javax.net.ssl.SSLSocketFactory.class) 86 .newInstance(testSSLContext.clientContext.getSocketFactory()); 87 } 88 89 /** 90 * We had bugs where proxy system properties weren't being honored. 91 * http://b/3254717 92 */ 93 public void testConnectViaProxyUsingProxySystemProperty() throws Exception { 94 testConnectViaProxy(ProxyConfig.PROXY_SYSTEM_PROPERTY); 95 } 96 97 public void testConnectViaProxyUsingHttpProxySystemProperty() throws Exception { 98 testConnectViaProxy(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY); 99 } 100 101 public void testConnectViaProxyUsingRequestParameter() throws Exception { 102 testConnectViaProxy(ProxyConfig.REQUEST_PARAMETER); 103 } 104 105 public void testConnectViaProxyUsingClientParameter() throws Exception { 106 testConnectViaProxy(ProxyConfig.CLIENT_PARAMETER); 107 } 108 109 /** 110 * http://code.google.com/p/android/issues/detail?id=2690 111 */ 112 private void testConnectViaProxy(ProxyConfig proxyConfig) throws Exception { 113 MockResponse mockResponse = new MockResponse() 114 .setResponseCode(200) 115 .setBody("this response comes via a proxy"); 116 server.enqueue(mockResponse); 117 server.play(); 118 119 HttpClient httpProxyClient = newHttpClient(); 120 121 HttpGet request = new HttpGet("http://android.com/foo"); 122 proxyConfig.configure(server, httpProxyClient, request); 123 124 HttpResponse response = httpProxyClient.execute(request); 125 assertEquals("this response comes via a proxy", contentToString(response)); 126 127 RecordedRequest get = server.takeRequest(); 128 assertEquals("GET http://android.com/foo HTTP/1.1", get.getRequestLine()); 129 assertContains(get.getHeaders(), "Host: android.com"); 130 } 131 132 public void testConnectViaHttpProxyToHttpsUsingProxySystemProperty() throws Exception { 133 testConnectViaHttpProxyToHttps(ProxyConfig.PROXY_SYSTEM_PROPERTY); 134 } 135 136 public void testConnectViaHttpProxyToHttpsUsingHttpsProxySystemProperty() throws Exception { 137 testConnectViaHttpProxyToHttps(ProxyConfig.HTTPS_PROXY_SYSTEM_PROPERTY); 138 } 139 140 public void testConnectViaHttpProxyToHttpsUsingClientParameter() throws Exception { 141 testConnectViaHttpProxyToHttps(ProxyConfig.CLIENT_PARAMETER); 142 } 143 144 public void testConnectViaHttpProxyToHttpsUsingRequestParameter() throws Exception { 145 testConnectViaHttpProxyToHttps(ProxyConfig.REQUEST_PARAMETER); 146 } 147 148 private void testConnectViaHttpProxyToHttps(ProxyConfig proxyConfig) throws Exception { 149 TestSSLContext testSSLContext = TestSSLContext.create(); 150 151 server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); 152 server.enqueue(new MockResponse() 153 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 154 .clearHeaders()); 155 server.enqueue(new MockResponse() 156 .setResponseCode(200) 157 .setBody("this response comes via a secure proxy")); 158 server.play(); 159 160 HttpClient httpProxyClient = newHttpClient(); 161 SSLSocketFactory sslSocketFactory = newSslSocketFactory(testSSLContext); 162 sslSocketFactory.setHostnameVerifier(new AllowAllHostnameVerifier()); 163 httpProxyClient.getConnectionManager().getSchemeRegistry() 164 .register(new Scheme("https", sslSocketFactory, 443)); 165 166 HttpGet request = new HttpGet("https://android.com/foo"); 167 proxyConfig.configure(server, httpProxyClient, request); 168 169 HttpResponse response = httpProxyClient.execute(request); 170 assertEquals("this response comes via a secure proxy", contentToString(response)); 171 172 RecordedRequest connect = server.takeRequest(); 173 assertEquals("Connect line failure on proxy " + proxyConfig, 174 "CONNECT android.com:443 HTTP/1.1", connect.getRequestLine()); 175 assertContains(connect.getHeaders(), "Host: android.com"); 176 177 RecordedRequest get = server.takeRequest(); 178 assertEquals("GET /foo HTTP/1.1", get.getRequestLine()); 179 assertContains(get.getHeaders(), "Host: android.com"); 180 } 181 182 public void testClientParamPreferredOverSystemProperty() throws Exception { 183 testParamPreferredOverSystemProperty(ProxyConfig.CLIENT_PARAMETER); 184 } 185 186 public void testRequestParamPreferredOverSystemProperty() throws Exception { 187 testParamPreferredOverSystemProperty(ProxyConfig.REQUEST_PARAMETER); 188 } 189 190 private void testParamPreferredOverSystemProperty(ProxyConfig proxyConfig) throws Exception { 191 server.enqueue(new MockResponse().setBody("Via request parameter proxy!")); 192 server.play(); 193 System.setProperty("http.proxyHost", "proxy.foo"); 194 System.setProperty("http.proxyPort", "8080"); 195 196 HttpClient client = newHttpClient(); 197 HttpGet request = new HttpGet("http://origin.foo/bar"); 198 proxyConfig.configure(server, client, request); 199 HttpResponse response = client.execute(request); 200 assertEquals("Via request parameter proxy!", contentToString(response)); 201 202 RecordedRequest recordedRequest = server.takeRequest(); 203 assertEquals("GET http://origin.foo/bar HTTP/1.1", recordedRequest.getRequestLine()); 204 } 205 206 public void testExplicitNoProxyCancelsSystemProperty() throws Exception { 207 server.enqueue(new MockResponse().setBody("Via the origin server!")); 208 server.play(); 209 System.setProperty("http.proxyHost", "proxy.foo"); 210 System.setProperty("http.proxyPort", "8080"); 211 212 HttpClient client = newHttpClient(); 213 HttpGet request = new HttpGet(server.getUrl("/bar").toURI()); 214 request.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, ConnRouteParams.NO_HOST); 215 HttpResponse response = client.execute(request); 216 assertEquals("Via the origin server!", contentToString(response)); 217 218 RecordedRequest recordedRequest = server.takeRequest(); 219 assertEquals("GET /bar HTTP/1.1", recordedRequest.getRequestLine()); 220 } 221 222 // http://b/5372438 223 public void testRetryWithProxy() throws Exception { 224 server.enqueue(new MockResponse() 225 .setSocketPolicy(SocketPolicy.DISCONNECT_AT_START)); 226 server.play(); 227 228 HttpClient httpProxyClient = newHttpClient(); 229 HttpGet request = new HttpGet("http://android.com/foo"); 230 ProxyConfig.REQUEST_PARAMETER.configure(server, httpProxyClient, request); 231 232 try { 233 httpProxyClient.execute(request); 234 fail(); 235 } catch (IOException expected) { 236 } 237 } 238 239 enum ProxyConfig { 240 PROXY_SYSTEM_PROPERTY() { 241 @Override void configure(MockWebServer server, HttpClient client, HttpRequest request) { 242 System.setProperty("proxyHost", "localhost"); 243 System.setProperty("proxyPort", Integer.toString(server.getPort())); 244 } 245 }, 246 HTTP_PROXY_SYSTEM_PROPERTY() { 247 @Override void configure(MockWebServer server, HttpClient client, HttpRequest request) { 248 System.setProperty("http.proxyHost", "localhost"); 249 System.setProperty("http.proxyPort", Integer.toString(server.getPort())); 250 } 251 }, 252 HTTPS_PROXY_SYSTEM_PROPERTY() { 253 @Override void configure(MockWebServer server, HttpClient client, HttpRequest request) { 254 System.setProperty("https.proxyHost", "localhost"); 255 System.setProperty("https.proxyPort", Integer.toString(server.getPort())); 256 } 257 }, 258 CLIENT_PARAMETER() { 259 @Override void configure(MockWebServer server, HttpClient client, HttpRequest request) { 260 client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, 261 new HttpHost("localhost", server.getPort())); 262 } 263 }, 264 REQUEST_PARAMETER() { 265 @Override void configure(MockWebServer server, HttpClient client, HttpRequest request) { 266 request.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, 267 new HttpHost("localhost", server.getPort())); 268 } 269 }; 270 271 abstract void configure(MockWebServer proxy, HttpClient client, HttpRequest request); 272 } 273 274 private void assertContains(List<String> headers, String header) { 275 assertTrue(headers.toString(), headers.contains(header)); 276 } 277 278 private String contentToString(HttpResponse response) throws IOException { 279 StringWriter writer = new StringWriter(); 280 char[] buffer = new char[1024]; 281 Reader reader = new InputStreamReader(response.getEntity().getContent()); 282 int length; 283 while ((length = reader.read(buffer)) != -1) { 284 writer.write(buffer, 0, length); 285 } 286 reader.close(); 287 return writer.toString(); 288 } 289 } 290