diff options
author | Brian Carlstrom <bdc@google.com> | 2011-05-26 23:49:18 -0700 |
---|---|---|
committer | Brian Carlstrom <bdc@google.com> | 2011-05-26 23:49:18 -0700 |
commit | 2ab014f88b412ceb4a0593d0934edb536ef1baa8 (patch) | |
tree | 9e14fbbbaea8cc671e30cea7cc2b27afed73e530 /support | |
parent | 4ccd262b321e206768f652aff1507d4e1258c007 (diff) | |
parent | 73d0c8232b23ad74af5cc198143603ff6ee5236b (diff) | |
download | libcore-2ab014f88b412ceb4a0593d0934edb536ef1baa8.zip libcore-2ab014f88b412ceb4a0593d0934edb536ef1baa8.tar.gz libcore-2ab014f88b412ceb4a0593d0934edb536ef1baa8.tar.bz2 |
Merge remote branch 'goog/dalvik-dev' into dalvik-dev-to-master
Diffstat (limited to 'support')
6 files changed, 162 insertions, 353 deletions
diff --git a/support/src/test/java/libcore/net/http/HttpResponseCache.java b/support/src/test/java/libcore/net/http/HttpResponseCache.java deleted file mode 100644 index a551e27..0000000 --- a/support/src/test/java/libcore/net/http/HttpResponseCache.java +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * 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 libcore.net.http; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.CacheRequest; -import java.net.CacheResponse; -import java.net.HttpURLConnection; -import java.net.ResponseCache; -import java.net.SecureCacheResponse; -import java.net.URI; -import java.net.URLConnection; -import java.security.Principal; -import java.security.cert.Certificate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLPeerUnverifiedException; - -/** - * Cache all responses in memory by URI. - * - * TODO: disk storage, tuning knobs, LRU - * TODO: move this class to android.util - */ -public final class HttpResponseCache extends ResponseCache { - private final Map<URI, Entry> entries = new HashMap<URI, Entry>(); - private int abortCount; - private int successCount; - private int hitCount; - private int missCount; - - @Override public synchronized CacheResponse get(URI uri, String requestMethod, - Map<String, List<String>> requestHeaders) throws IOException { - Entry entry = entries.get(uri); - if (entry == null) { - missCount++; - return null; - } - - if (!requestMethod.equals(entry.requestMethod)) { - return null; - } - - // RawHeaders headers = RawHeaders.fromMultimap(entry.responseHeaders); - - hitCount++; - return entry.asResponse(); - } - - @Override public CacheRequest put(URI uri, URLConnection urlConnection) - throws IOException { - if (!(urlConnection instanceof HttpURLConnection)) { - return null; - } - - HttpURLConnection httpConnection = (HttpURLConnection) urlConnection; - String requestMethod = httpConnection.getRequestMethod(); - - // Invalidate the cache on POST, PUT and DELETE. - if (requestMethod.equals("POST") || requestMethod.equals("PUT") - || requestMethod.equals("DELETE")) { - entries.remove(uri); - } - - /* - * Don't cache non-GET responses. We're technically allowed to cache - * HEAD requests and some POST requests, but the complexity of doing so - * is high and the benefit is low. - */ - if (!requestMethod.equals("GET")) { - return null; - } - - // For implementation simplicity, don't cache responses that have a Vary field. - if (httpConnection.getHeaderField("Vary") != null) { - return null; - } - - return new Entry(uri, httpConnection).asRequest(); - } - - public synchronized Map<URI, Entry> getContents() { - return new HashMap<URI, Entry>(entries); - } - - /** - * Returns the number of requests that were aborted before they were closed. - */ - public synchronized int getAbortCount() { - return abortCount; - } - - /** - * Returns the number of requests that were closed successfully. - */ - public synchronized int getSuccessCount() { - return successCount; - } - - /** - * Returns the number of responses served by the cache. - */ - public synchronized int getHitCount() { - return hitCount; - } - - /** - * Returns the number of responses that couldn't be served by the cache. - */ - public synchronized int getMissCount() { - return missCount; - } - - public final class Entry { - private final ByteArrayOutputStream body = new ByteArrayOutputStream() { - private boolean closed; - @Override public void close() throws IOException { - synchronized (HttpResponseCache.this) { - if (closed) { - return; - } - - super.close(); - entries.put(uri, Entry.this); - successCount++; - closed = true; - } - } - }; - - private final String requestMethod; - private final Map<String, List<String>> responseHeaders; - private final URI uri; - private final CacheResponse cacheResponse; - - private Entry(URI uri, HttpURLConnection connection) { - this.uri = uri; - this.requestMethod = connection.getRequestMethod(); - this.responseHeaders = deepCopy(connection.getHeaderFields()); - this.cacheResponse = connection instanceof HttpsURLConnection - ? new SecureCacheResponseImpl(responseHeaders, body, - (HttpsURLConnection) connection) - : new CacheResponseImpl(responseHeaders, body); - } - - public CacheRequest asRequest() { - return new CacheRequest() { - private boolean aborted; - @Override public void abort() { - synchronized (HttpResponseCache.this) { - if (aborted) { - return; - } - - abortCount++; - aborted = true; - } - } - @Override public OutputStream getBody() throws IOException { - return body; - } - }; - } - - public CacheResponse asResponse() { - return cacheResponse; - } - - public byte[] getBytes() { - return body.toByteArray(); - } - } - - private final class CacheResponseImpl extends CacheResponse { - private final Map<String, List<String>> headers; - private final ByteArrayOutputStream bytesOut; - - public CacheResponseImpl(Map<String, List<String>> headers, - ByteArrayOutputStream bytesOut) { - this.headers = headers; - this.bytesOut = bytesOut; - } - - @Override public Map<String, List<String>> getHeaders() { - return deepCopy(headers); - } - - @Override public InputStream getBody() { - return new ByteArrayInputStream(bytesOut.toByteArray()); - } - } - - private final class SecureCacheResponseImpl extends SecureCacheResponse { - private final Map<String, List<String>> headers; - private final ByteArrayOutputStream bytesOut; - private final String cipherSuite; - private final Certificate[] localCertificates; - private final List<Certificate> serverCertificates; - private final Principal peerPrincipal; - private final Principal localPrincipal; - - public SecureCacheResponseImpl(Map<String, List<String>> headers, - ByteArrayOutputStream bytesOut, - HttpsURLConnection httpsConnection) { - this.headers = headers; - this.bytesOut = bytesOut; - - /* - * Retrieve the fields eagerly to avoid needing a strong - * reference to the connection. We do acrobatics for the two - * methods that can throw so that the cache response also - * throws. - */ - List<Certificate> serverCertificatesNonFinal = null; - try { - serverCertificatesNonFinal = Arrays.asList( - httpsConnection.getServerCertificates()); - } catch (SSLPeerUnverifiedException ignored) { - } - Principal peerPrincipalNonFinal = null; - try { - peerPrincipalNonFinal = httpsConnection.getPeerPrincipal(); - } catch (SSLPeerUnverifiedException ignored) { - } - this.cipherSuite = httpsConnection.getCipherSuite(); - this.localCertificates = httpsConnection.getLocalCertificates(); - this.serverCertificates = serverCertificatesNonFinal; - this.peerPrincipal = peerPrincipalNonFinal; - this.localPrincipal = httpsConnection.getLocalPrincipal(); - } - - @Override public Map<String, List<String>> getHeaders() { - return deepCopy(headers); - } - - @Override public InputStream getBody() { - return new ByteArrayInputStream(bytesOut.toByteArray()); - } - - @Override public String getCipherSuite() { - return cipherSuite; - } - - @Override public List<Certificate> getLocalCertificateChain() { - return localCertificates != null - ? Arrays.asList(localCertificates.clone()) - : null; - } - - @Override public List<Certificate> getServerCertificateChain() - throws SSLPeerUnverifiedException { - if (serverCertificates == null) { - throw new SSLPeerUnverifiedException(null); - } - return new ArrayList<Certificate>(serverCertificates); - } - - @Override public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { - if (peerPrincipal == null) { - throw new SSLPeerUnverifiedException(null); - } - return peerPrincipal; - } - - @Override public Principal getLocalPrincipal() { - return localPrincipal; - } - } - - private static Map<String, List<String>> deepCopy(Map<String, List<String>> input) { - Map<String, List<String>> result = new LinkedHashMap<String, List<String>>(input); - for (Map.Entry<String, List<String>> entry : result.entrySet()) { - entry.setValue(new ArrayList<String>(entry.getValue())); - } - return result; - } -} diff --git a/support/src/test/java/tests/http/MockWebServer.java b/support/src/test/java/tests/http/MockWebServer.java index 2d8215c..a130dde 100644 --- a/support/src/test/java/tests/http/MockWebServer.java +++ b/support/src/test/java/tests/http/MockWebServer.java @@ -251,9 +251,7 @@ public final class MockWebServer { Socket socket; if (sslSocketFactory != null) { if (tunnelProxy) { - if (!processOneRequest(raw.getInputStream(), raw.getOutputStream(), raw)) { - throw new IllegalStateException("Tunnel without any CONNECT!"); - } + createTunnel(); } socket = sslSocketFactory.createSocket( raw, raw.getInetAddress().getHostAddress(), raw.getPort(), true); @@ -283,6 +281,22 @@ public final class MockWebServer { } /** + * Respond to CONNECT requests until a SWITCH_TO_SSL_AT_END response + * is dispatched. + */ + private void createTunnel() throws IOException, InterruptedException { + while (true) { + MockResponse connect = responseQueue.peek(); + if (!processOneRequest(raw.getInputStream(), raw.getOutputStream(), raw)) { + throw new IllegalStateException("Tunnel without any CONNECT!"); + } + if (connect.getSocketPolicy() == SocketPolicy.UPGRADE_TO_SSL_AT_END) { + return; + } + } + } + + /** * Reads a request and writes its response. Returns true if a request * was processed. */ diff --git a/support/src/test/java/tests/http/SocketPolicy.java b/support/src/test/java/tests/http/SocketPolicy.java index 549c65d..820b400 100644 --- a/support/src/test/java/tests/http/SocketPolicy.java +++ b/support/src/test/java/tests/http/SocketPolicy.java @@ -34,6 +34,12 @@ public enum SocketPolicy { DISCONNECT_AT_END, /** + * Wrap the socket with SSL at the completion of this request/response + * pair. Used for CONNECT messages to tunnel SSL over an HTTP proxy. + */ + UPGRADE_TO_SSL_AT_END, + + /** * Request immediate close of connection without even reading the * request. * diff --git a/support/src/test/java/tests/io/MockOs.java b/support/src/test/java/tests/io/MockOs.java new file mode 100644 index 0000000..0cfe836 --- /dev/null +++ b/support/src/test/java/tests/io/MockOs.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * 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 tests.io; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashMap; +import java.util.Map; +import libcore.io.ErrnoException; +import libcore.io.Libcore; +import libcore.io.Os; +import libcore.io.OsConstants; + +/** + * A mocking interceptor that wraps another {@link Os} to add faults. This can + * be useful to test otherwise hard-to-test scenarios such as a full disk. + */ +public final class MockOs { + private final InheritableThreadLocal<Map<String, Deque<InvocationHandler>>> handlers + = new InheritableThreadLocal<Map<String, Deque<InvocationHandler>>>() { + @Override protected Map<String, Deque<InvocationHandler>> initialValue() { + return new HashMap<String, Deque<InvocationHandler>>(); + } + }; + + private Os delegate; + private final InvocationHandler delegateHandler = new InvocationHandler() { + @Override public Object invoke(Object o, Method method, Object[] args) throws Throwable { + try { + return method.invoke(delegate, args); + } catch (InvocationTargetException e) { + throw e.getCause(); + } + } + }; + + private final InvocationHandler invocationHandler = new InvocationHandler() { + @Override public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + InvocationHandler handler = getHandlers(method.getName()).poll(); + if (handler == null) { + handler = delegateHandler; + } + return handler.invoke(proxy, method, args); + } + }; + + private final Os mockOs = (Os) Proxy.newProxyInstance(MockOs.class.getClassLoader(), + new Class[] { Os.class }, invocationHandler); + + public void install() { + if (delegate != null) { + throw new IllegalStateException("MockOs already installed!"); + } + delegate = Libcore.os; + Libcore.os = mockOs; + } + + public void uninstall() { + if (delegate == null) { + throw new IllegalStateException("MockOs not installed!"); + } + Libcore.os = delegate; + } + + /** + * Returns the invocation handlers to handle upcoming invocations of + * {@code methodName}. If empty, calls will be handled by the delegate. + */ + public Deque<InvocationHandler> getHandlers(String methodName) { + Map<String, Deque<InvocationHandler>> threadFaults = handlers.get(); + Deque<InvocationHandler> result = threadFaults.get(methodName); + if (result == null) { + result = new ArrayDeque<InvocationHandler>(); + threadFaults.put(methodName, result); + } + return result; + } + + /** + * Enqueues the specified number of normal operations. Useful to delay + * faults. + */ + public void enqueueNormal(String methodName, int count) { + Deque<InvocationHandler> handlers = getHandlers(methodName); + for (int i = 0; i < count; i++) { + handlers.add(delegateHandler); + } + } + + public void enqueueFault(String methodName) { + enqueueFault(methodName, OsConstants.EIO); + } + + public void enqueueFault(String methodName, final int errno) { + getHandlers(methodName).add(new InvocationHandler() { + @Override public Object invoke(Object proxy, Method method, Object[] args) { + throw new ErrnoException(method.getName(), errno); + } + }); + } +} diff --git a/support/src/test/java/tests/net/StuckServer.java b/support/src/test/java/tests/net/StuckServer.java index 4230f17..eababce 100644 --- a/support/src/test/java/tests/net/StuckServer.java +++ b/support/src/test/java/tests/net/StuckServer.java @@ -40,6 +40,24 @@ public final class StuckServer { } } + public void unblockAfterMs(final int ms) { + Thread t = new Thread(new Runnable() { + @Override public void run() { + try { + Thread.sleep(ms); + for (Socket client : clients) { + client.close(); + } + clients.clear(); + clients.add(serverSocket.accept()); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + }); + t.start(); + } + public InetSocketAddress getLocalSocketAddress() { return (InetSocketAddress) serverSocket.getLocalSocketAddress(); } diff --git a/support/src/test/java/tests/support/Support_Configuration.java b/support/src/test/java/tests/support/Support_Configuration.java index fb5b468..fed0bc8 100644 --- a/support/src/test/java/tests/support/Support_Configuration.java +++ b/support/src/test/java/tests/support/Support_Configuration.java @@ -46,8 +46,6 @@ public class Support_Configuration { public static String HomeAddressSoftware = "Jetty(6.0.x)"; - public static String ProxyServerTestHost = "jcltest.apache.org"; - public static String SocksServerTestHost = "jcltest.apache.org"; public static int SocksServerTestPort = 1080; @@ -75,15 +73,7 @@ public class Support_Configuration { public static byte[] InetTestCaddr = { 9, 26, -56, -111 }; - public static final String HomeAddress6 = "jcltest6.apache.org"; - - public static String IPv6GlobalAddressJcl4 = "FE80:0000:0000:0000:020D:60FF:FE0F:A776%4"; // this - - public static String ProxyServerTestHostIPv6 = "jcltest6.apache.org"; - - public static String InetTestIP6 = "fe80::20d:60ff:fe24:7410"; - - public static String InetTestIP6LO = "::1"; + public static String IPv6GlobalAddressJcl4 = "2001:4860:8004::67"; // ipv6.google.com // ip address that resolves to a host that is not present on the local // network @@ -131,12 +121,6 @@ public class Support_Configuration { public static long URLConnectionDate = 929106872000L; - public static boolean RunCommTests = false; - - public static String Port1 = "COM1"; - - public static String Port2 = "COM2"; - static Hashtable<String, String> props = null; static { loadProperties(); @@ -149,9 +133,6 @@ public class Support_Configuration { Hashtable<String, String> props = new Hashtable<String, String>(); String iniName = System.getProperty("test.ini.file", "JCLAuto.ini"); - if (System.getProperty("test.comm") != null) { - RunCommTests = true; - } try { in = new FileInputStream(iniName); @@ -204,11 +185,6 @@ public class Support_Configuration { HomeAddressSoftware = value; } - value = props.get("ProxyServerTestHost"); - if (value != null) { - ProxyServerTestHost = value; - } - value = props.get("SocksServerTestHost"); if (value != null) { SocksServerTestHost = value; @@ -313,31 +289,6 @@ public class Support_Configuration { URLConnectionDate = Long.parseLong(value); } - value = props.get("Port1"); - if (value != null) { - Port1 = value; - } - - value = props.get("Port2"); - if (value != null) { - Port2 = value; - } - - value = props.get("InetTestIP6"); - if (value != null) { - InetTestIP6 = value; - } - - value = props.get("InetTestIP6LO"); - if (value != null) { - InetTestIP6LO = value; - } - - value = props.get("ProxyServerTestHostIPv6"); - if (value != null) { - ProxyServerTestHostIPv6 = value; - } - value = props.get("ResolvedNotExistingHost"); if (value != null) { ResolvedNotExistingHost = value; |