aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeil Fuller <nfuller@google.com>2015-02-11 09:33:10 +0000
committerNeil Fuller <nfuller@google.com>2015-02-11 10:15:24 +0000
commit3be78b8b0ca13d9e05e2327acb8d8654f719a3f6 (patch)
tree68d9e2f18e43ccc4e75bf9fca8cdbd4db44640a4
parent576c6599deeae3ca87e545c67ce765d9c4062b14 (diff)
downloadokhttp-3be78b8b0ca13d9e05e2327acb8d8654f719a3f6.tar.gz
A rollup of recent upstream commits for OkHttp
squareup/okhttp commits from: 0a197466608681593cc9be9487965a0b1d5c244c to: b609edd07864d7191dcda8ba1f6c833c9fe170ad squareup/okio commits from: 654ddf5e8f6311fda77e429c22d5e0e15f713b8d to 82358df7f09e18aa42348836c614212085bbf045 Changes that might affect Android: 1) Cache control request headers: If-None-Match or If-Modified-Since sent, never both. 2) Make okhttp behave more like a private, not a shared cache. 3) SSLPeerUnverifiedException now thrown on hostname verification errors, not IOException. Change-Id: I3a2e8ae9bebfec84eaf8eb2aaa70085fa40fadd5
-rw-r--r--mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java48
-rw-r--r--okhttp-android-support/src/test/java/com/squareup/okhttp/internal/huc/ResponseCacheTest.java3
-rw-r--r--okhttp-tests/src/test/java/com/squareup/okhttp/CacheControlTest.java25
-rw-r--r--okhttp-tests/src/test/java/com/squareup/okhttp/CacheTest.java30
-rw-r--r--okhttp-tests/src/test/java/com/squareup/okhttp/ConnectionSpecTest.java10
-rw-r--r--okhttp-tests/src/test/java/com/squareup/okhttp/InterceptorTest.java59
-rw-r--r--okhttp-tests/src/test/java/com/squareup/okhttp/OkHttpClientTest.java29
-rw-r--r--okhttp-urlconnection/src/test/java/com/squareup/okhttp/UrlConnectionCacheTest.java2
-rw-r--r--okhttp/src/main/java/com/squareup/okhttp/CacheControl.java17
-rw-r--r--okhttp/src/main/java/com/squareup/okhttp/Connection.java3
-rw-r--r--okhttp/src/main/java/com/squareup/okhttp/ConnectionSpec.java33
-rw-r--r--okhttp/src/main/java/com/squareup/okhttp/Dispatcher.java4
-rw-r--r--okhttp/src/main/java/com/squareup/okhttp/OkHttpClient.java12
-rw-r--r--okhttp/src/main/java/com/squareup/okhttp/internal/http/CacheStrategy.java14
-rw-r--r--okio/okio/src/main/java/okio/ByteString.java31
-rw-r--r--okio/okio/src/main/java/okio/Sink.java5
-rw-r--r--okio/okio/src/test/java/okio/ByteStringTest.java31
17 files changed, 300 insertions, 56 deletions
diff --git a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java
index a63bbd4..39fbf6f 100644
--- a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java
+++ b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java
@@ -116,7 +116,7 @@ public final class MockWebServer {
private Dispatcher dispatcher = new QueueDispatcher();
private int port = -1;
- private InetAddress inetAddress;
+ private InetSocketAddress inetSocketAddress;
private boolean protocolNegotiationEnabled = true;
private List<Protocol> protocols
= Util.immutableList(Protocol.HTTP_2, Protocol.SPDY_3, Protocol.HTTP_1_1);
@@ -132,15 +132,18 @@ public final class MockWebServer {
}
public String getHostName() {
- if (inetAddress == null) throw new IllegalStateException("Call start() before getHostName()");
- return inetAddress.getHostName();
+ if (inetSocketAddress == null) {
+ throw new IllegalStateException("Call start() before getHostName()");
+ }
+ return inetSocketAddress.getHostName();
}
public Proxy toProxyAddress() {
- if (inetAddress == null) {
+ if (inetSocketAddress == null) {
throw new IllegalStateException("Call start() before toProxyAddress()");
}
- return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(inetAddress, getPort()));
+ InetSocketAddress address = new InetSocketAddress(inetSocketAddress.getAddress(), getPort());
+ return new Proxy(Proxy.Type.HTTP, address);
}
/**
@@ -276,22 +279,45 @@ public final class MockWebServer {
}
/**
- * Starts the server.
+ * Starts the server on the loopback interface for the given port.
*
* @param port the port to listen to, or 0 for any available port. Automated
* tests should always use port 0 to avoid flakiness when a specific port
* is unavailable.
*/
public void start(int port) throws IOException {
+ start(InetAddress.getByName("localhost"), port);
+ }
+
+ /**
+ * Starts the server on the given address and port.
+ *
+ * @param inetAddress the address to create the server socket on
+ *
+ * @param port the port to listen to, or 0 for any available port. Automated
+ * tests should always use port 0 to avoid flakiness when a specific port
+ * is unavailable.
+ */
+ public void start(InetAddress inetAddress, int port) throws IOException {
+ start(new InetSocketAddress(inetAddress, port));
+ }
+
+ /**
+ * Starts the server and binds to the given socket address.
+ *
+ * @param inetSocketAddress the socket address to bind the server on
+ */
+ private void start(InetSocketAddress inetSocketAddress) throws IOException {
if (executor != null) throw new IllegalStateException("start() already called");
executor = Executors.newCachedThreadPool(Util.threadFactory("MockWebServer", false));
- inetAddress = InetAddress.getByName("localhost");
+ this.inetSocketAddress = inetSocketAddress;
serverSocket = serverSocketFactory.createServerSocket();
- serverSocket.setReuseAddress(port != 0); // Reuse the port if the port number was specified.
- serverSocket.bind(new InetSocketAddress(inetAddress, port), 50);
+ // Reuse if the user specified a port
+ serverSocket.setReuseAddress(inetSocketAddress.getPort() != 0);
+ serverSocket.bind(inetSocketAddress, 50);
- this.port = serverSocket.getLocalPort();
- executor.execute(new NamedRunnable("MockWebServer %s", this.port) {
+ port = serverSocket.getLocalPort();
+ executor.execute(new NamedRunnable("MockWebServer %s", port) {
@Override protected void execute() {
try {
logger.info(MockWebServer.this + " starting to accept connections");
diff --git a/okhttp-android-support/src/test/java/com/squareup/okhttp/internal/huc/ResponseCacheTest.java b/okhttp-android-support/src/test/java/com/squareup/okhttp/internal/huc/ResponseCacheTest.java
index 5fdb2fc..3c91fb5 100644
--- a/okhttp-android-support/src/test/java/com/squareup/okhttp/internal/huc/ResponseCacheTest.java
+++ b/okhttp-android-support/src/test/java/com/squareup/okhttp/internal/huc/ResponseCacheTest.java
@@ -77,7 +77,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -628,7 +627,7 @@ public final class ResponseCacheTest {
.addHeader("Last-Modified: " + lastModifiedDate)
.addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
assertEquals("v1", conditionalRequest.getHeader("If-None-Match"));
- assertEquals(lastModifiedDate, conditionalRequest.getHeader("If-Modified-Since"));
+ assertNull(conditionalRequest.getHeader("If-Modified-Since"));
}
@Test public void etagAndExpirationDateInTheFuture() throws Exception {
diff --git a/okhttp-tests/src/test/java/com/squareup/okhttp/CacheControlTest.java b/okhttp-tests/src/test/java/com/squareup/okhttp/CacheControlTest.java
index e08adf3..5d13767 100644
--- a/okhttp-tests/src/test/java/com/squareup/okhttp/CacheControlTest.java
+++ b/okhttp-tests/src/test/java/com/squareup/okhttp/CacheControlTest.java
@@ -33,6 +33,7 @@ public final class CacheControlTest {
assertFalse(cacheControl.noStore());
assertEquals(-1, cacheControl.maxAgeSeconds());
assertEquals(-1, cacheControl.sMaxAgeSeconds());
+ assertFalse(cacheControl.isPrivate());
assertFalse(cacheControl.isPublic());
assertFalse(cacheControl.mustRevalidate());
assertEquals(-1, cacheControl.maxStaleSeconds());
@@ -62,6 +63,7 @@ public final class CacheControlTest {
// These members are accessible to response headers only.
assertEquals(-1, cacheControl.sMaxAgeSeconds());
+ assertFalse(cacheControl.isPrivate());
assertFalse(cacheControl.isPublic());
assertFalse(cacheControl.mustRevalidate());
}
@@ -83,7 +85,7 @@ public final class CacheControlTest {
}
@Test public void parse() throws Exception {
- String header = "no-cache, no-store, max-age=1, s-maxage=2, public, must-revalidate, "
+ String header = "no-cache, no-store, max-age=1, s-maxage=2, private, public, must-revalidate, "
+ "max-stale=3, min-fresh=4, only-if-cached, no-transform";
CacheControl cacheControl = CacheControl.parse(new Headers.Builder()
.set("Cache-Control", header)
@@ -92,6 +94,7 @@ public final class CacheControlTest {
assertTrue(cacheControl.noStore());
assertEquals(1, cacheControl.maxAgeSeconds());
assertEquals(2, cacheControl.sMaxAgeSeconds());
+ assertTrue(cacheControl.isPrivate());
assertTrue(cacheControl.isPublic());
assertTrue(cacheControl.mustRevalidate());
assertEquals(3, cacheControl.maxStaleSeconds());
@@ -101,6 +104,26 @@ public final class CacheControlTest {
assertEquals(header, cacheControl.toString());
}
+ @Test public void parseIgnoreCacheControlExtensions() throws Exception {
+ // Example from http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.6
+ String header = "private, community=\"UCI\"";
+ CacheControl cacheControl = CacheControl.parse(new Headers.Builder()
+ .set("Cache-Control", header)
+ .build());
+ assertFalse(cacheControl.noCache());
+ assertFalse(cacheControl.noStore());
+ assertEquals(-1, cacheControl.maxAgeSeconds());
+ assertEquals(-1, cacheControl.sMaxAgeSeconds());
+ assertTrue(cacheControl.isPrivate());
+ assertFalse(cacheControl.isPublic());
+ assertFalse(cacheControl.mustRevalidate());
+ assertEquals(-1, cacheControl.maxStaleSeconds());
+ assertEquals(-1, cacheControl.minFreshSeconds());
+ assertFalse(cacheControl.onlyIfCached());
+ assertFalse(cacheControl.noTransform());
+ assertEquals(header, cacheControl.toString());
+ }
+
@Test public void parseCacheControlAndPragmaAreCombined() {
Headers headers =
Headers.of("Cache-Control", "max-age=12", "Pragma", "must-revalidate", "Pragma", "public");
diff --git a/okhttp-tests/src/test/java/com/squareup/okhttp/CacheTest.java b/okhttp-tests/src/test/java/com/squareup/okhttp/CacheTest.java
index d422143..af0f506 100644
--- a/okhttp-tests/src/test/java/com/squareup/okhttp/CacheTest.java
+++ b/okhttp-tests/src/test/java/com/squareup/okhttp/CacheTest.java
@@ -23,20 +23,6 @@ import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import com.squareup.okhttp.mockwebserver.RecordedRequest;
import com.squareup.okhttp.mockwebserver.rule.MockWebServerRule;
-import okio.Buffer;
-import okio.BufferedSink;
-import okio.BufferedSource;
-import okio.GzipSink;
-import okio.Okio;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSession;
import java.io.File;
import java.io.IOException;
import java.net.CookieHandler;
@@ -59,6 +45,19 @@ import java.util.NoSuchElementException;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import okio.Buffer;
+import okio.BufferedSink;
+import okio.BufferedSource;
+import okio.GzipSink;
+import okio.Okio;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
import static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
import static org.junit.Assert.assertEquals;
@@ -819,6 +818,7 @@ public final class CacheTest {
assertEquals("v1", conditionalRequest.getHeader("If-None-Match"));
}
+ /** If both If-Modified-Since and If-None-Match conditions apply, send only If-None-Match. */
@Test public void etagAndExpirationDateInThePast() throws Exception {
String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
@@ -826,7 +826,7 @@ public final class CacheTest {
.addHeader("Last-Modified: " + lastModifiedDate)
.addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
assertEquals("v1", conditionalRequest.getHeader("If-None-Match"));
- assertEquals(lastModifiedDate, conditionalRequest.getHeader("If-Modified-Since"));
+ assertNull(conditionalRequest.getHeader("If-Modified-Since"));
}
@Test public void etagAndExpirationDateInTheFuture() throws Exception {
diff --git a/okhttp-tests/src/test/java/com/squareup/okhttp/ConnectionSpecTest.java b/okhttp-tests/src/test/java/com/squareup/okhttp/ConnectionSpecTest.java
index df1e58f..2267c2a 100644
--- a/okhttp-tests/src/test/java/com/squareup/okhttp/ConnectionSpecTest.java
+++ b/okhttp-tests/src/test/java/com/squareup/okhttp/ConnectionSpecTest.java
@@ -166,6 +166,16 @@ public final class ConnectionSpecTest {
assertEquals(expectedCipherSet, expectedCipherSet);
}
+ @Test
+ public void tls_stringCiphersAndVersions() throws Exception {
+ // Supporting arbitrary input strings allows users to enable suites and versions that are not
+ // yet known to the library, but are supported by the platform.
+ ConnectionSpec tlsSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
+ .cipherSuites("MAGIC-CIPHER")
+ .tlsVersions("TLS9k")
+ .build();
+ }
+
private static Set<String> createSet(String... values) {
return new LinkedHashSet<String>(Arrays.asList(values));
}
diff --git a/okhttp-tests/src/test/java/com/squareup/okhttp/InterceptorTest.java b/okhttp-tests/src/test/java/com/squareup/okhttp/InterceptorTest.java
index 8d16b07..d186f4a 100644
--- a/okhttp-tests/src/test/java/com/squareup/okhttp/InterceptorTest.java
+++ b/okhttp-tests/src/test/java/com/squareup/okhttp/InterceptorTest.java
@@ -371,6 +371,65 @@ public final class InterceptorTest {
assertEquals(response.body().string(), "b");
}
+ /** Make sure interceptors can interact with the OkHttp client. */
+ @Test public void interceptorMakesAnUnrelatedRequest() throws Exception {
+ server.enqueue(new MockResponse().setBody("a")); // Fetched by interceptor.
+ server.enqueue(new MockResponse().setBody("b")); // Fetched directly.
+
+ client.interceptors().add(new Interceptor() {
+ @Override public Response intercept(Chain chain) throws IOException {
+ if (chain.request().url().getPath().equals("/b")) {
+ Request requestA = new Request.Builder()
+ .url(server.getUrl("/a"))
+ .build();
+ Response responseA = client.newCall(requestA).execute();
+ assertEquals("a", responseA.body().string());
+ }
+
+ return chain.proceed(chain.request());
+ }
+ });
+
+ Request requestB = new Request.Builder()
+ .url(server.getUrl("/b"))
+ .build();
+ Response responseB = client.newCall(requestB).execute();
+ assertEquals("b", responseB.body().string());
+ }
+
+ /** Make sure interceptors can interact with the OkHttp client asynchronously. */
+ @Test public void interceptorMakesAnUnrelatedAsyncRequest() throws Exception {
+ server.enqueue(new MockResponse().setBody("a")); // Fetched by interceptor.
+ server.enqueue(new MockResponse().setBody("b")); // Fetched directly.
+
+ client.interceptors().add(new Interceptor() {
+ @Override public Response intercept(Chain chain) throws IOException {
+ if (chain.request().url().getPath().equals("/b")) {
+ Request requestA = new Request.Builder()
+ .url(server.getUrl("/a"))
+ .build();
+
+ try {
+ RecordingCallback callbackA = new RecordingCallback();
+ client.newCall(requestA).enqueue(callbackA);
+ callbackA.await(requestA.url()).assertBody("a");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ return chain.proceed(chain.request());
+ }
+ });
+
+ Request requestB = new Request.Builder()
+ .url(server.getUrl("/b"))
+ .build();
+ RecordingCallback callbackB = new RecordingCallback();
+ client.newCall(requestB).enqueue(callbackB);
+ callbackB.await(requestB.url()).assertBody("b");
+ }
+
private RequestBody uppercase(final RequestBody original) {
return new RequestBody() {
@Override public MediaType contentType() {
diff --git a/okhttp-tests/src/test/java/com/squareup/okhttp/OkHttpClientTest.java b/okhttp-tests/src/test/java/com/squareup/okhttp/OkHttpClientTest.java
index 0bb8d1a..1fdaf1b 100644
--- a/okhttp-tests/src/test/java/com/squareup/okhttp/OkHttpClientTest.java
+++ b/okhttp-tests/src/test/java/com/squareup/okhttp/OkHttpClientTest.java
@@ -32,6 +32,7 @@ import java.net.URLConnection;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
import javax.net.SocketFactory;
import org.junit.After;
import org.junit.Test;
@@ -56,6 +57,34 @@ public final class OkHttpClientTest {
Authenticator.setDefault(DEFAULT_AUTHENTICATOR);
}
+ @Test public void timeoutValidRange() {
+ OkHttpClient client = new OkHttpClient();
+ try {
+ client.setConnectTimeout(1, TimeUnit.NANOSECONDS);
+ } catch (IllegalArgumentException ignored) {
+ }
+ try {
+ client.setWriteTimeout(1, TimeUnit.NANOSECONDS);
+ } catch (IllegalArgumentException ignored) {
+ }
+ try {
+ client.setReadTimeout(1, TimeUnit.NANOSECONDS);
+ } catch (IllegalArgumentException ignored) {
+ }
+ try {
+ client.setConnectTimeout(365, TimeUnit.DAYS);
+ } catch (IllegalArgumentException ignored) {
+ }
+ try {
+ client.setWriteTimeout(365, TimeUnit.DAYS);
+ } catch (IllegalArgumentException ignored) {
+ }
+ try {
+ client.setReadTimeout(365, TimeUnit.DAYS);
+ } catch (IllegalArgumentException ignored) {
+ }
+ }
+
/** Confirm that {@code copyWithDefaults} gets expected constant values. */
@Test public void copyWithDefaultsWhenDefaultIsAConstant() throws Exception {
OkHttpClient client = new OkHttpClient().copyWithDefaults();
diff --git a/okhttp-urlconnection/src/test/java/com/squareup/okhttp/UrlConnectionCacheTest.java b/okhttp-urlconnection/src/test/java/com/squareup/okhttp/UrlConnectionCacheTest.java
index c46fd07..db0ed8f 100644
--- a/okhttp-urlconnection/src/test/java/com/squareup/okhttp/UrlConnectionCacheTest.java
+++ b/okhttp-urlconnection/src/test/java/com/squareup/okhttp/UrlConnectionCacheTest.java
@@ -717,7 +717,7 @@ public final class UrlConnectionCacheTest {
.addHeader("Last-Modified: " + lastModifiedDate)
.addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
assertEquals("v1", conditionalRequest.getHeader("If-None-Match"));
- assertEquals(lastModifiedDate, conditionalRequest.getHeader("If-Modified-Since"));
+ assertNull(conditionalRequest.getHeader("If-Modified-Since"));
}
@Test public void etagAndExpirationDateInTheFuture() throws Exception {
diff --git a/okhttp/src/main/java/com/squareup/okhttp/CacheControl.java b/okhttp/src/main/java/com/squareup/okhttp/CacheControl.java
index f7d9f30..2ee8982 100644
--- a/okhttp/src/main/java/com/squareup/okhttp/CacheControl.java
+++ b/okhttp/src/main/java/com/squareup/okhttp/CacheControl.java
@@ -34,6 +34,7 @@ public final class CacheControl {
private final boolean noStore;
private final int maxAgeSeconds;
private final int sMaxAgeSeconds;
+ private final boolean isPrivate;
private final boolean isPublic;
private final boolean mustRevalidate;
private final int maxStaleSeconds;
@@ -44,12 +45,13 @@ public final class CacheControl {
String headerValue; // Lazily computed, if absent.
private CacheControl(boolean noCache, boolean noStore, int maxAgeSeconds, int sMaxAgeSeconds,
- boolean isPublic, boolean mustRevalidate, int maxStaleSeconds, int minFreshSeconds,
- boolean onlyIfCached, boolean noTransform, String headerValue) {
+ boolean isPrivate, boolean isPublic, boolean mustRevalidate, int maxStaleSeconds,
+ int minFreshSeconds, boolean onlyIfCached, boolean noTransform, String headerValue) {
this.noCache = noCache;
this.noStore = noStore;
this.maxAgeSeconds = maxAgeSeconds;
this.sMaxAgeSeconds = sMaxAgeSeconds;
+ this.isPrivate = isPrivate;
this.isPublic = isPublic;
this.mustRevalidate = mustRevalidate;
this.maxStaleSeconds = maxStaleSeconds;
@@ -64,6 +66,7 @@ public final class CacheControl {
this.noStore = builder.noStore;
this.maxAgeSeconds = builder.maxAgeSeconds;
this.sMaxAgeSeconds = -1;
+ this.isPrivate = false;
this.isPublic = false;
this.mustRevalidate = false;
this.maxStaleSeconds = builder.maxStaleSeconds;
@@ -106,6 +109,10 @@ public final class CacheControl {
return sMaxAgeSeconds;
}
+ public boolean isPrivate() {
+ return isPrivate;
+ }
+
public boolean isPublic() {
return isPublic;
}
@@ -146,6 +153,7 @@ public final class CacheControl {
boolean noStore = false;
int maxAgeSeconds = -1;
int sMaxAgeSeconds = -1;
+ boolean isPrivate = false;
boolean isPublic = false;
boolean mustRevalidate = false;
int maxStaleSeconds = -1;
@@ -212,6 +220,8 @@ public final class CacheControl {
maxAgeSeconds = HeaderParser.parseSeconds(parameter, -1);
} else if ("s-maxage".equalsIgnoreCase(directive)) {
sMaxAgeSeconds = HeaderParser.parseSeconds(parameter, -1);
+ } else if ("private".equalsIgnoreCase(directive)) {
+ isPrivate = true;
} else if ("public".equalsIgnoreCase(directive)) {
isPublic = true;
} else if ("must-revalidate".equalsIgnoreCase(directive)) {
@@ -231,7 +241,7 @@ public final class CacheControl {
if (!canUseHeaderValue) {
headerValue = null;
}
- return new CacheControl(noCache, noStore, maxAgeSeconds, sMaxAgeSeconds, isPublic,
+ return new CacheControl(noCache, noStore, maxAgeSeconds, sMaxAgeSeconds, isPrivate, isPublic,
mustRevalidate, maxStaleSeconds, minFreshSeconds, onlyIfCached, noTransform, headerValue);
}
@@ -246,6 +256,7 @@ public final class CacheControl {
if (noStore) result.append("no-store, ");
if (maxAgeSeconds != -1) result.append("max-age=").append(maxAgeSeconds).append(", ");
if (sMaxAgeSeconds != -1) result.append("s-maxage=").append(sMaxAgeSeconds).append(", ");
+ if (isPrivate) result.append("private, ");
if (isPublic) result.append("public, ");
if (mustRevalidate) result.append("must-revalidate, ");
if (maxStaleSeconds != -1) result.append("max-stale=").append(maxStaleSeconds).append(", ");
diff --git a/okhttp/src/main/java/com/squareup/okhttp/Connection.java b/okhttp/src/main/java/com/squareup/okhttp/Connection.java
index 8d8586e..7dddc3a 100644
--- a/okhttp/src/main/java/com/squareup/okhttp/Connection.java
+++ b/okhttp/src/main/java/com/squareup/okhttp/Connection.java
@@ -32,6 +32,7 @@ import java.net.Socket;
import java.net.URL;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSocket;
import okio.Source;
@@ -254,7 +255,7 @@ public final class Connection {
// Verify that the socket's certificates are acceptable for the target host.
if (!route.address.hostnameVerifier.verify(route.address.uriHost, sslSocket.getSession())) {
X509Certificate cert = (X509Certificate) sslSocket.getSession().getPeerCertificates()[0];
- throw new IOException("Hostname " + route.address.uriHost + " not verified:"
+ throw new SSLPeerUnverifiedException("Hostname " + route.address.uriHost + " not verified:"
+ "\n certificate: " + CertificatePinner.pin(cert)
+ "\n DN: " + cert.getSubjectDN().getName()
+ "\n subjectAltNames: " + OkHostnameVerifier.allSubjectAltNames(cert));
diff --git a/okhttp/src/main/java/com/squareup/okhttp/ConnectionSpec.java b/okhttp/src/main/java/com/squareup/okhttp/ConnectionSpec.java
index e905052..254bcb1 100644
--- a/okhttp/src/main/java/com/squareup/okhttp/ConnectionSpec.java
+++ b/okhttp/src/main/java/com/squareup/okhttp/ConnectionSpec.java
@@ -176,6 +176,7 @@ public final class ConnectionSpec {
@Override public boolean equals(Object other) {
if (!(other instanceof ConnectionSpec)) return false;
+ if (other == this) return true;
ConnectionSpec that = (ConnectionSpec) other;
if (this.tls != that.tls) return false;
@@ -237,12 +238,20 @@ public final class ConnectionSpec {
for (int i = 0; i < cipherSuites.length; i++) {
strings[i] = cipherSuites[i].javaName;
}
-
- return cipherSuites(strings);
+ this.cipherSuites = strings;
+ return this;
}
- Builder cipherSuites(String[] cipherSuites) {
- this.cipherSuites = cipherSuites; // No defensive copy.
+ public Builder cipherSuites(String... cipherSuites) {
+ if (!tls) throw new IllegalStateException("no cipher suites for cleartext connections");
+
+ if (cipherSuites == null) {
+ this.cipherSuites = null;
+ } else {
+ // This makes a defensive copy!
+ this.cipherSuites = cipherSuites.clone();
+ }
+
return this;
}
@@ -254,12 +263,20 @@ public final class ConnectionSpec {
for (int i = 0; i < tlsVersions.length; i++) {
strings[i] = tlsVersions[i].javaName;
}
-
- return tlsVersions(strings);
+ this.tlsVersions = strings;
+ return this;
}
- Builder tlsVersions(String... tlsVersions) {
- this.tlsVersions = tlsVersions; // No defensive copy.
+ public Builder tlsVersions(String... tlsVersions) {
+ if (!tls) throw new IllegalStateException("no TLS versions for cleartext connections");
+
+ if (tlsVersions == null) {
+ this.tlsVersions = null;
+ } else {
+ // This makes a defensive copy!
+ this.tlsVersions = tlsVersions.clone();
+ }
+
return this;
}
diff --git a/okhttp/src/main/java/com/squareup/okhttp/Dispatcher.java b/okhttp/src/main/java/com/squareup/okhttp/Dispatcher.java
index 95eb7b0..a696c0c 100644
--- a/okhttp/src/main/java/com/squareup/okhttp/Dispatcher.java
+++ b/okhttp/src/main/java/com/squareup/okhttp/Dispatcher.java
@@ -22,7 +22,7 @@ import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -59,7 +59,7 @@ public final class Dispatcher {
public synchronized ExecutorService getExecutorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
- new LinkedBlockingQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
+ new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
diff --git a/okhttp/src/main/java/com/squareup/okhttp/OkHttpClient.java b/okhttp/src/main/java/com/squareup/okhttp/OkHttpClient.java
index 56b55f9..a8b60db 100644
--- a/okhttp/src/main/java/com/squareup/okhttp/OkHttpClient.java
+++ b/okhttp/src/main/java/com/squareup/okhttp/OkHttpClient.java
@@ -203,7 +203,8 @@ public class OkHttpClient implements Cloneable {
}
/**
- * Sets the default connect timeout for new connections. A value of 0 means no timeout.
+ * Sets the default connect timeout for new connections. A value of 0 means no timeout, otherwise
+ * values must be between 1 and {@link Integer#MAX_VALUE} when converted to milliseconds.
*
* @see URLConnection#setConnectTimeout(int)
*/
@@ -212,6 +213,7 @@ public class OkHttpClient implements Cloneable {
if (unit == null) throw new IllegalArgumentException("unit == null");
long millis = unit.toMillis(timeout);
if (millis > Integer.MAX_VALUE) throw new IllegalArgumentException("Timeout too large.");
+ if (millis == 0 && timeout > 0) throw new IllegalArgumentException("Timeout too small.");
connectTimeout = (int) millis;
}
@@ -221,7 +223,8 @@ public class OkHttpClient implements Cloneable {
}
/**
- * Sets the default read timeout for new connections. A value of 0 means no timeout.
+ * Sets the default read timeout for new connections. A value of 0 means no timeout, otherwise
+ * values must be between 1 and {@link Integer#MAX_VALUE} when converted to milliseconds.
*
* @see URLConnection#setReadTimeout(int)
*/
@@ -230,6 +233,7 @@ public class OkHttpClient implements Cloneable {
if (unit == null) throw new IllegalArgumentException("unit == null");
long millis = unit.toMillis(timeout);
if (millis > Integer.MAX_VALUE) throw new IllegalArgumentException("Timeout too large.");
+ if (millis == 0 && timeout > 0) throw new IllegalArgumentException("Timeout too small.");
readTimeout = (int) millis;
}
@@ -239,13 +243,15 @@ public class OkHttpClient implements Cloneable {
}
/**
- * Sets the default write timeout for new connections. A value of 0 means no timeout.
+ * Sets the default write timeout for new connections. A value of 0 means no timeout, otherwise
+ * values must be between 1 and {@link Integer#MAX_VALUE} when converted to milliseconds.
*/
public final void setWriteTimeout(long timeout, TimeUnit unit) {
if (timeout < 0) throw new IllegalArgumentException("timeout < 0");
if (unit == null) throw new IllegalArgumentException("unit == null");
long millis = unit.toMillis(timeout);
if (millis > Integer.MAX_VALUE) throw new IllegalArgumentException("Timeout too large.");
+ if (millis == 0 && timeout > 0) throw new IllegalArgumentException("Timeout too small.");
writeTimeout = (int) millis;
}
diff --git a/okhttp/src/main/java/com/squareup/okhttp/internal/http/CacheStrategy.java b/okhttp/src/main/java/com/squareup/okhttp/internal/http/CacheStrategy.java
index 7af04aa..3f07edd 100644
--- a/okhttp/src/main/java/com/squareup/okhttp/internal/http/CacheStrategy.java
+++ b/okhttp/src/main/java/com/squareup/okhttp/internal/http/CacheStrategy.java
@@ -67,10 +67,12 @@ public final class CacheStrategy {
case HTTP_MOVED_TEMP:
case HTTP_TEMP_REDIRECT:
// These codes can only be cached with the right response headers.
+ // http://tools.ietf.org/html/rfc7234#section-3
+ // s-maxage is not checked because OkHttp is a private cache that should ignore s-maxage.
if (response.header("Expires") != null
|| response.cacheControl().maxAgeSeconds() != -1
- || response.cacheControl().sMaxAgeSeconds() != -1
- || response.cacheControl().isPublic()) {
+ || response.cacheControl().isPublic()
+ || response.cacheControl().isPrivate()) {
break;
}
// Fall-through.
@@ -223,16 +225,14 @@ public final class CacheStrategy {
Request.Builder conditionalRequestBuilder = request.newBuilder();
- if (lastModified != null) {
+ if (etag != null) {
+ conditionalRequestBuilder.header("If-None-Match", etag);
+ } else if (lastModified != null) {
conditionalRequestBuilder.header("If-Modified-Since", lastModifiedString);
} else if (servedDate != null) {
conditionalRequestBuilder.header("If-Modified-Since", servedDateString);
}
- if (etag != null) {
- conditionalRequestBuilder.header("If-None-Match", etag);
- }
-
Request conditionalRequest = conditionalRequestBuilder.build();
return hasConditions(conditionalRequest)
? new CacheStrategy(conditionalRequest, cacheResponse)
diff --git a/okio/okio/src/main/java/okio/ByteString.java b/okio/okio/src/main/java/okio/ByteString.java
index a42fbe6..d8335d1 100644
--- a/okio/okio/src/main/java/okio/ByteString.java
+++ b/okio/okio/src/main/java/okio/ByteString.java
@@ -211,6 +211,37 @@ public final class ByteString implements Serializable {
return this;
}
+ /**
+ * Returns a byte string that is a substring of this byte string, beginning at the specified
+ * index until the end of this string. Returns this byte string if {@code beginIndex} is 0.
+ */
+ public ByteString substring(int beginIndex) {
+ return substring(beginIndex, data.length);
+ }
+
+ /**
+ * Returns a byte string that is a substring of this byte string, beginning at the specified
+ * {@code beginIndex} and ends at the specified {@code endIndex}. Returns this byte string if
+ * {@code beginIndex} is 0 and {@code endIndex} is the length of this byte string.
+ */
+ public ByteString substring(int beginIndex, int endIndex) {
+ if (beginIndex < 0) throw new IllegalArgumentException("beginIndex < 0");
+ if (endIndex > data.length) {
+ throw new IllegalArgumentException("endIndex > length(" + data.length + ")");
+ }
+
+ int subLen = endIndex - beginIndex;
+ if (subLen < 0) throw new IllegalArgumentException("endIndex < beginIndex");
+
+ if ((beginIndex == 0) && (endIndex == data.length)) {
+ return this;
+ }
+
+ byte[] copy = new byte[subLen];
+ System.arraycopy(data, beginIndex, copy, 0, subLen);
+ return new ByteString(copy);
+ }
+
/** Returns the byte at {@code pos}. */
public byte getByte(int pos) {
return data[pos];
diff --git a/okio/okio/src/main/java/okio/Sink.java b/okio/okio/src/main/java/okio/Sink.java
index 38c46de..370e83b 100644
--- a/okio/okio/src/main/java/okio/Sink.java
+++ b/okio/okio/src/main/java/okio/Sink.java
@@ -16,6 +16,7 @@
package okio;
import java.io.Closeable;
+import java.io.Flushable;
import java.io.IOException;
/**
@@ -47,12 +48,12 @@ import java.io.IOException;
* Use {@link Okio#sink} to adapt an {@code OutputStream} to a sink. Use {@link
* BufferedSink#outputStream} to adapt a sink to an {@code OutputStream}.
*/
-public interface Sink extends Closeable {
+public interface Sink extends Closeable, Flushable {
/** Removes {@code byteCount} bytes from {@code source} and appends them to this. */
void write(Buffer source, long byteCount) throws IOException;
/** Pushes all buffered bytes to their final destination. */
- void flush() throws IOException;
+ @Override void flush() throws IOException;
/** Returns the timeout for this sink. */
Timeout timeout();
diff --git a/okio/okio/src/test/java/okio/ByteStringTest.java b/okio/okio/src/test/java/okio/ByteStringTest.java
index 528ed05..ec98f7d 100644
--- a/okio/okio/src/test/java/okio/ByteStringTest.java
+++ b/okio/okio/src/test/java/okio/ByteStringTest.java
@@ -117,6 +117,37 @@ public class ByteStringTest {
@Test public void toAsciiStartsUppercaseEndsLowercase() throws Exception {
assertEquals(ByteString.encodeUtf8("ABCD"), ByteString.encodeUtf8("ABcd").toAsciiUppercase());
}
+
+ @Test public void substring() throws Exception {
+ ByteString byteString = ByteString.encodeUtf8("Hello, World!");
+
+ assertEquals(byteString.substring(0), byteString);
+ assertEquals(byteString.substring(0, 5), ByteString.encodeUtf8("Hello"));
+ assertEquals(byteString.substring(7), ByteString.encodeUtf8("World!"));
+ assertEquals(byteString.substring(6, 6), ByteString.encodeUtf8(""));
+ }
+
+ @Test public void substringWithInvalidBounds() throws Exception {
+ ByteString byteString = ByteString.encodeUtf8("Hello, World!");
+
+ try {
+ byteString.substring(-1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ byteString.substring(0, 14);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ byteString.substring(8, 7);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
@Test public void write() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();