aboutsummaryrefslogtreecommitdiff
path: root/mockwebserver
diff options
context:
space:
mode:
authorTobias Thierer <tobiast@google.com>2016-06-24 19:04:17 +0100
committerTobias Thierer <tobiast@google.com>2016-06-29 16:23:09 +0100
commit6c251e20f00c7574b217bd4351ac81666f574380 (patch)
tree2d66a76721f4c8170b990742922675f32ad38122 /mockwebserver
parent68e16131f12f0174c4c9e5785f6e63297cd02adf (diff)
downloadokhttp-6c251e20f00c7574b217bd4351ac81666f574380.tar.gz
Update OkHttp to 2.7.5 and advance okio by one commit.
This brings OkHttp and okio exactly in line with upstream commits with no local changes. Corresponding upstream commits: okhttp:6e236ce3b80f21369dc544f0e1053ff71be8689b (= parent-2.7.5) okio: 02481cc0cc84bc92e3eab6d5212a226496f56a7e The okio commit differs from the one in the previous pull from Sep 2015 (AOSP commit 71b9f47b26fb57ac3e436a19519c6e3ec70e86eb) only by a single upstream commit, the switch to 8 KiB segments. That commit was previously cherry-picked in AOSP. This CL will temporarily revert the AOSP changes to okio, but those AOSP changes to okio will be reapplied in the subsequent CL. Compilation and tests do not pass after this CL, they will only pass at the end of the chain of 11 CLs going in at the same time. 9 of these 11 CLs are in external/okhttp, the others affect libcore and frameworks/base. Details of behavioural changes introduced by this upgrade are at: https://docs.google.com/document/d/19PF3Exd_q32gAGCiRFWRf0Pq_xrIWs-cRViHkFTxJg8/edit This CL includes files that are not used in Android, such as - top level dot files (.travis.yml etc.) - subdirectories okurl, okhttp-apache, samples, which aren't used - tests in okhttp-hpacktests, okhttp-ws-tests that aren't run or test functionality that we aren't used Test: I've run the following tests *at the end* of the chain of commits, in cts-tradefed: 1.) run cts -p android.core.tests.libcore.package.harmony_java_net 2.) run cts -c libcore.java.net.URLConnectionTest 3.) run cts -p android.core.tests.libcore.package.okhttp 4.) run cts -p android.core.tests.libcore.package.libcore 1.-3.) all passed 4.) had 24 unrelated failures per b/29496407 and b/29744850 Change-Id: Id798d6cf49fa4a7a4ab8ae3b699a38104bf42db3
Diffstat (limited to 'mockwebserver')
-rw-r--r--mockwebserver/README.md2
-rw-r--r--mockwebserver/pom.xml2
-rw-r--r--mockwebserver/src/main/java/com/squareup/okhttp/internal/HeldCertificate.java143
-rw-r--r--mockwebserver/src/main/java/com/squareup/okhttp/internal/SslContextBuilder.java119
-rw-r--r--mockwebserver/src/main/java/com/squareup/okhttp/internal/framed/FramedServer.java9
-rw-r--r--mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockResponse.java16
-rw-r--r--mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java45
-rw-r--r--mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/QueueDispatcher.java4
-rw-r--r--mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/SocketPolicy.java5
-rw-r--r--mockwebserver/src/test/java/com/squareup/okhttp/mockwebserver/MockWebServerTest.java33
10 files changed, 283 insertions, 95 deletions
diff --git a/mockwebserver/README.md b/mockwebserver/README.md
index 3fd6ca6..ba40bf5 100644
--- a/mockwebserver/README.md
+++ b/mockwebserver/README.md
@@ -42,7 +42,7 @@ public void test() throws Exception {
server.start();
// Ask the server for its URL. You'll need this to make HTTP requests.
- URL baseUrl = server.url("/v1/chat/");
+ HttpUrl baseUrl = server.url("/v1/chat/");
// Exercise your application code, which should make those HTTP requests.
// Responses are returned in the same order that they are enqueued.
diff --git a/mockwebserver/pom.xml b/mockwebserver/pom.xml
index 9ef5211..7681ad1 100644
--- a/mockwebserver/pom.xml
+++ b/mockwebserver/pom.xml
@@ -6,7 +6,7 @@
<parent>
<groupId>com.squareup.okhttp</groupId>
<artifactId>parent</artifactId>
- <version>2.6.0-SNAPSHOT</version>
+ <version>2.7.5</version>
</parent>
<artifactId>mockwebserver</artifactId>
diff --git a/mockwebserver/src/main/java/com/squareup/okhttp/internal/HeldCertificate.java b/mockwebserver/src/main/java/com/squareup/okhttp/internal/HeldCertificate.java
new file mode 100644
index 0000000..2fff99c
--- /dev/null
+++ b/mockwebserver/src/main/java/com/squareup/okhttp/internal/HeldCertificate.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2016 Square, Inc.
+ *
+ * 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 com.squareup.okhttp.internal;
+
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.UUID;
+import javax.security.auth.x500.X500Principal;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.x509.X509V3CertificateGenerator;
+
+/**
+ * A certificate and its private key. This can be used on the server side by HTTPS servers, or on
+ * the client side to verify those HTTPS servers. A held certificate can also be used to sign other
+ * held certificates, as done in practice by certificate authorities.
+ */
+public final class HeldCertificate {
+ public final X509Certificate certificate;
+ public final KeyPair keyPair;
+
+ public HeldCertificate(X509Certificate certificate, KeyPair keyPair) {
+ this.certificate = certificate;
+ this.keyPair = keyPair;
+ }
+
+ public static final class Builder {
+ static {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ private final long duration = 1000L * 60 * 60 * 24; // One day.
+ private String hostname;
+ private String serialNumber = "1";
+ private KeyPair keyPair;
+ private HeldCertificate issuedBy;
+ private int maxIntermediateCas;
+
+ public Builder serialNumber(String serialNumber) {
+ this.serialNumber = serialNumber;
+ return this;
+ }
+
+ /**
+ * Set this certificate's name. Typically this is the URL hostname for TLS certificates. This is
+ * the CN (common name) in the certificate. Will be a random string if no value is provided.
+ */
+ public Builder commonName(String hostname) {
+ this.hostname = hostname;
+ return this;
+ }
+
+ public Builder keyPair(KeyPair keyPair) {
+ this.keyPair = keyPair;
+ return this;
+ }
+
+ /**
+ * Set the certificate that signs this certificate. If unset, a self-signed certificate will be
+ * generated.
+ */
+ public Builder issuedBy(HeldCertificate signedBy) {
+ this.issuedBy = signedBy;
+ return this;
+ }
+
+ /**
+ * Set this certificate to be a certificate authority, with up to {@code maxIntermediateCas}
+ * intermediate certificate authorities beneath it.
+ */
+ public Builder ca(int maxIntermediateCas) {
+ this.maxIntermediateCas = maxIntermediateCas;
+ return this;
+ }
+
+ public HeldCertificate build() throws GeneralSecurityException {
+ // Subject, public & private keys for this certificate.
+ KeyPair heldKeyPair = keyPair != null
+ ? keyPair
+ : generateKeyPair();
+ X500Principal subject = hostname != null
+ ? new X500Principal("CN=" + hostname)
+ : new X500Principal("CN=" + UUID.randomUUID());
+
+ // Subject, public & private keys for this certificate's signer. It may be self signed!
+ KeyPair signedByKeyPair;
+ X500Principal signedByPrincipal;
+ if (issuedBy != null) {
+ signedByKeyPair = issuedBy.keyPair;
+ signedByPrincipal = issuedBy.certificate.getSubjectX500Principal();
+ } else {
+ signedByKeyPair = heldKeyPair;
+ signedByPrincipal = subject;
+ }
+
+ // Generate & sign the certificate.
+ long now = System.currentTimeMillis();
+ X509V3CertificateGenerator generator = new X509V3CertificateGenerator();
+ generator.setSerialNumber(new BigInteger(serialNumber));
+ generator.setIssuerDN(signedByPrincipal);
+ generator.setNotBefore(new Date(now));
+ generator.setNotAfter(new Date(now + duration));
+ generator.setSubjectDN(subject);
+ generator.setPublicKey(heldKeyPair.getPublic());
+ generator.setSignatureAlgorithm("SHA256WithRSAEncryption");
+
+ if (maxIntermediateCas > 0) {
+ generator.addExtension(X509Extensions.BasicConstraints, true,
+ new BasicConstraints(maxIntermediateCas));
+ }
+
+ X509Certificate certificate = generator.generateX509Certificate(
+ signedByKeyPair.getPrivate(), "BC");
+ return new HeldCertificate(certificate, heldKeyPair);
+ }
+
+ public KeyPair generateKeyPair() throws GeneralSecurityException {
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
+ keyPairGenerator.initialize(1024, new SecureRandom());
+ return keyPairGenerator.generateKeyPair();
+ }
+ }
+}
diff --git a/mockwebserver/src/main/java/com/squareup/okhttp/internal/SslContextBuilder.java b/mockwebserver/src/main/java/com/squareup/okhttp/internal/SslContextBuilder.java
index 546d660..fd1d020 100644
--- a/mockwebserver/src/main/java/com/squareup/okhttp/internal/SslContextBuilder.java
+++ b/mockwebserver/src/main/java/com/squareup/okhttp/internal/SslContextBuilder.java
@@ -18,24 +18,18 @@ package com.squareup.okhttp.internal;
import java.io.IOException;
import java.io.InputStream;
-import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.SecureRandom;
-import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
-import java.util.Date;
+import java.util.ArrayList;
+import java.util.List;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
-import javax.security.auth.x500.X500Principal;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.x509.X509V3CertificateGenerator;
/**
* Constructs an SSL context for testing. This uses Bouncy Castle to generate a
@@ -45,51 +39,70 @@ import org.bouncycastle.x509.X509V3CertificateGenerator;
* reuse SSL context instances where possible.
*/
public final class SslContextBuilder {
- static {
- Security.addProvider(new BouncyCastleProvider());
- }
-
- private static final long ONE_DAY_MILLIS = 1000L * 60 * 60 * 24;
private static SSLContext localhost; // Lazily initialized.
- private final String hostName;
- private long notBefore = System.currentTimeMillis();
- private long notAfter = System.currentTimeMillis() + ONE_DAY_MILLIS;
+ /** Returns a new SSL context for this host's current localhost address. */
+ public static synchronized SSLContext localhost() {
+ if (localhost != null) return localhost;
+
+ try {
+ // Generate a self-signed cert for the server to serve and the client to trust.
+ HeldCertificate heldCertificate = new HeldCertificate.Builder()
+ .serialNumber("1")
+ .commonName(InetAddress.getByName("localhost").getHostName())
+ .build();
+
+ localhost = new SslContextBuilder()
+ .certificateChain(heldCertificate)
+ .addTrustedCertificate(heldCertificate.certificate)
+ .build();
+
+ return localhost;
+ } catch (GeneralSecurityException e) {
+ throw new RuntimeException(e);
+ } catch (UnknownHostException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private HeldCertificate[] chain;
+ private List<X509Certificate> trustedCertificates = new ArrayList<>();
/**
- * @param hostName the subject of the host. For TLS this should be the
- * domain name that the client uses to identify the server.
+ * Configure the certificate chain to use when serving HTTPS responses. The first certificate
+ * in this chain is the server's certificate, further certificates are included in the handshake
+ * so the client can build a trusted path to a CA certificate.
*/
- public SslContextBuilder(String hostName) {
- this.hostName = hostName;
+ public SslContextBuilder certificateChain(HeldCertificate... chain) {
+ this.chain = chain;
+ return this;
}
- /** Returns a new SSL context for this host's current localhost address. */
- public static synchronized SSLContext localhost() {
- if (localhost == null) {
- try {
- localhost = new SslContextBuilder(InetAddress.getByName("localhost").getHostName()).build();
- } catch (GeneralSecurityException e) {
- throw new RuntimeException(e);
- } catch (UnknownHostException e) {
- throw new RuntimeException(e);
- }
- }
- return localhost;
+ /**
+ * Add a certificate authority that this client trusts. Servers that provide certificate chains
+ * signed by these roots (or their intermediates) will be accepted.
+ */
+ public SslContextBuilder addTrustedCertificate(X509Certificate certificate) {
+ trustedCertificates.add(certificate);
+ return this;
}
public SSLContext build() throws GeneralSecurityException {
+ // Put the certificate in a key store.
char[] password = "password".toCharArray();
+ KeyStore keyStore = newEmptyKeyStore(password);
- // Generate public and private keys and use them to make a self-signed certificate.
- KeyPair keyPair = generateKeyPair();
- X509Certificate certificate = selfSignedCertificate(keyPair, "1");
+ if (chain != null) {
+ Certificate[] certificates = new Certificate[chain.length];
+ for (int i = 0; i < chain.length; i++) {
+ certificates[i] = chain[i].certificate;
+ }
+ keyStore.setKeyEntry("private", chain[0].keyPair.getPrivate(), password, certificates);
+ }
- // Put 'em in a key store.
- KeyStore keyStore = newEmptyKeyStore(password);
- Certificate[] certificateChain = { certificate };
- keyStore.setKeyEntry("private", keyPair.getPrivate(), password, certificateChain);
- keyStore.setCertificateEntry("cert", certificate);
+ for (int i = 0; i < trustedCertificates.size(); i++) {
+ keyStore.setCertificateEntry("cert_" + i, trustedCertificates.get(i));
+ }
// Wrap it up in an SSL context.
KeyManagerFactory keyManagerFactory =
@@ -104,32 +117,6 @@ public final class SslContextBuilder {
return sslContext;
}
- public KeyPair generateKeyPair() throws GeneralSecurityException {
- KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
- keyPairGenerator.initialize(1024, new SecureRandom());
- return keyPairGenerator.generateKeyPair();
- }
-
- /**
- * Generates a certificate for {@code hostName} containing {@code keyPair}'s
- * public key, signed by {@code keyPair}'s private key.
- */
- @SuppressWarnings("deprecation") // use the old Bouncy Castle APIs to reduce dependencies.
- public X509Certificate selfSignedCertificate(KeyPair keyPair, String serialNumber)
- throws GeneralSecurityException {
- X509V3CertificateGenerator generator = new X509V3CertificateGenerator();
- X500Principal issuer = new X500Principal("CN=" + hostName);
- X500Principal subject = new X500Principal("CN=" + hostName);
- generator.setSerialNumber(new BigInteger(serialNumber));
- generator.setIssuerDN(issuer);
- generator.setNotBefore(new Date(notBefore));
- generator.setNotAfter(new Date(notAfter));
- generator.setSubjectDN(subject);
- generator.setPublicKey(keyPair.getPublic());
- generator.setSignatureAlgorithm("SHA256WithRSAEncryption");
- return generator.generateX509Certificate(keyPair.getPrivate(), "BC");
- }
-
private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException {
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
diff --git a/mockwebserver/src/main/java/com/squareup/okhttp/internal/framed/FramedServer.java b/mockwebserver/src/main/java/com/squareup/okhttp/internal/framed/FramedServer.java
index b95b64d..1574806 100644
--- a/mockwebserver/src/main/java/com/squareup/okhttp/internal/framed/FramedServer.java
+++ b/mockwebserver/src/main/java/com/squareup/okhttp/internal/framed/FramedServer.java
@@ -36,7 +36,7 @@ import okio.Okio;
import okio.Source;
/** A basic SPDY/HTTP_2 server that serves the contents of a local directory. */
-public final class FramedServer implements IncomingStreamHandler {
+public final class FramedServer extends FramedConnection.Listener {
static final Logger logger = Logger.getLogger(FramedServer.class.getName());
private final List<Protocol> framedProtocols =
@@ -65,9 +65,10 @@ public final class FramedServer implements IncomingStreamHandler {
if (protocol == null || !framedProtocols.contains(protocol)) {
throw new ProtocolException("Protocol " + protocol + " unsupported");
}
- FramedConnection framedConnection = new FramedConnection.Builder(false, sslSocket)
+ FramedConnection framedConnection = new FramedConnection.Builder(false)
+ .socket(sslSocket)
.protocol(protocol)
- .handler(this)
+ .listener(this)
.build();
framedConnection.sendConnectionPreface();
} catch (IOException e) {
@@ -89,7 +90,7 @@ public final class FramedServer implements IncomingStreamHandler {
return sslSocket;
}
- @Override public void receive(final FramedStream stream) throws IOException {
+ @Override public void onStream(final FramedStream stream) throws IOException {
try {
List<Header> requestHeaders = stream.getRequestHeaders();
String path = null;
diff --git a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockResponse.java b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockResponse.java
index bc43bd4..db68595 100644
--- a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockResponse.java
+++ b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockResponse.java
@@ -17,6 +17,7 @@ package com.squareup.okhttp.mockwebserver;
import com.squareup.okhttp.Headers;
import com.squareup.okhttp.internal.Internal;
+import com.squareup.okhttp.internal.framed.Settings;
import com.squareup.okhttp.ws.WebSocketListener;
import java.util.ArrayList;
import java.util.List;
@@ -42,6 +43,7 @@ public final class MockResponse implements Cloneable {
private TimeUnit bodyDelayUnit = TimeUnit.MILLISECONDS;
private List<PushPromise> promises = new ArrayList<>();
+ private Settings settings;
private WebSocketListener webSocketListener;
/** Creates a new mock response with an empty body. */
@@ -242,6 +244,20 @@ public final class MockResponse implements Cloneable {
}
/**
+ * When {@linkplain MockWebServer#setProtocols(java.util.List) protocols}
+ * include {@linkplain com.squareup.okhttp.Protocol#HTTP_2 HTTP/2}, this
+ * pushes {@code settings} before writing the response.
+ */
+ public MockResponse withSettings(Settings settings) {
+ this.settings = settings;
+ return this;
+ }
+
+ public Settings getSettings() {
+ return settings;
+ }
+
+ /**
* Attempts to perform a web socket upgrade on the connection. This will overwrite any previously
* set status or body.
*/
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 0e746d3..2c76398 100644
--- a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java
+++ b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java
@@ -29,7 +29,7 @@ import com.squareup.okhttp.internal.framed.ErrorCode;
import com.squareup.okhttp.internal.framed.FramedConnection;
import com.squareup.okhttp.internal.framed.FramedStream;
import com.squareup.okhttp.internal.framed.Header;
-import com.squareup.okhttp.internal.framed.IncomingStreamHandler;
+import com.squareup.okhttp.internal.framed.Settings;
import com.squareup.okhttp.internal.http.HttpMethod;
import com.squareup.okhttp.internal.ws.RealWebSocket;
import com.squareup.okhttp.internal.ws.WebSocketProtocol;
@@ -82,6 +82,7 @@ import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_AT_START;
+import static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_DURING_REQUEST_BODY;
import static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_DURING_RESPONSE_BODY;
import static com.squareup.okhttp.mockwebserver.SocketPolicy.FAIL_HANDSHAKE;
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -174,8 +175,10 @@ public final class MockWebServer implements TestRule {
}
public void setServerSocketFactory(ServerSocketFactory serverSocketFactory) {
- if (executor != null) throw new IllegalStateException(
- "setServerSocketFactory() must be called before start()");
+ if (executor != null) {
+ throw new IllegalStateException(
+ "setServerSocketFactory() must be called before start()");
+ }
this.serverSocketFactory = serverSocketFactory;
}
@@ -463,11 +466,12 @@ public final class MockWebServer implements TestRule {
}
if (protocol != Protocol.HTTP_1_1) {
- FramedSocketHandler framedSocketHandler = new FramedSocketHandler(socket, protocol);
- FramedConnection framedConnection =
- new FramedConnection.Builder(false, socket).protocol(protocol)
- .handler(framedSocketHandler)
- .build();
+ FramedSocketHandler framedSocketListener = new FramedSocketHandler(socket, protocol);
+ FramedConnection framedConnection = new FramedConnection.Builder(false)
+ .socket(socket)
+ .protocol(protocol)
+ .listener(framedSocketListener)
+ .build();
openFramedConnections.add(framedConnection);
openClientSockets.remove(socket);
return;
@@ -672,7 +676,7 @@ public final class MockWebServer implements TestRule {
final RealWebSocket webSocket =
new RealWebSocket(false /* is server */, source, sink, new SecureRandom(), replyExecutor,
listener, request.getPath()) {
- @Override protected void closeConnection() throws IOException {
+ @Override protected void close() throws IOException {
connectionClose.countDown();
}
};
@@ -704,6 +708,7 @@ public final class MockWebServer implements TestRule {
throw new RuntimeException(e);
}
+ replyExecutor.shutdown();
Util.closeQuietly(sink);
Util.closeQuietly(source);
}
@@ -754,8 +759,9 @@ public final class MockWebServer implements TestRule {
long periodDelayMs = policy.getThrottlePeriod(TimeUnit.MILLISECONDS);
long halfByteCount = byteCount / 2;
- boolean disconnectHalfway =
- !isRequest && policy.getSocketPolicy() == DISCONNECT_DURING_RESPONSE_BODY;
+ boolean disconnectHalfway = isRequest
+ ? policy.getSocketPolicy() == DISCONNECT_DURING_REQUEST_BODY
+ : policy.getSocketPolicy() == DISCONNECT_DURING_RESPONSE_BODY;
while (!socket.isClosed()) {
for (int b = 0; b < bytesPerPeriod; ) {
@@ -847,7 +853,7 @@ public final class MockWebServer implements TestRule {
}
/** Processes HTTP requests layered over framed protocols. */
- private class FramedSocketHandler implements IncomingStreamHandler {
+ private class FramedSocketHandler extends FramedConnection.Listener {
private final Socket socket;
private final Protocol protocol;
private final AtomicInteger sequenceNumber = new AtomicInteger();
@@ -857,7 +863,7 @@ public final class MockWebServer implements TestRule {
this.protocol = protocol;
}
- @Override public void receive(FramedStream stream) throws IOException {
+ @Override public void onStream(FramedStream stream) throws IOException {
RecordedRequest request = readRequest(stream);
requestQueue.add(request);
MockResponse response;
@@ -888,8 +894,14 @@ public final class MockWebServer implements TestRule {
path = value;
} else if (name.equals(Header.VERSION)) {
version = value;
- } else {
+ } else if (protocol == Protocol.SPDY_3) {
+ for (String s : value.split("\u0000", -1)) {
+ httpHeaders.add(name.utf8(), s);
+ }
+ } else if (protocol == Protocol.HTTP_2) {
httpHeaders.add(name.utf8(), value);
+ } else {
+ throw new IllegalStateException();
}
}
@@ -904,6 +916,11 @@ public final class MockWebServer implements TestRule {
}
private void writeResponse(FramedStream stream, MockResponse response) throws IOException {
+ Settings settings = response.getSettings();
+ if (settings != null) {
+ stream.getConnection().setSettings(settings);
+ }
+
if (response.getSocketPolicy() == SocketPolicy.NO_RESPONSE) {
return;
}
diff --git a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/QueueDispatcher.java b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/QueueDispatcher.java
index c9c206c..bb36cdf 100644
--- a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/QueueDispatcher.java
+++ b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/QueueDispatcher.java
@@ -18,12 +18,14 @@ package com.squareup.okhttp.mockwebserver;
import java.net.HttpURLConnection;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.logging.Logger;
/**
* Default dispatcher that processes a script of responses. Populate the script
* by calling {@link #enqueueResponse(MockResponse)}.
*/
public class QueueDispatcher extends Dispatcher {
+ private static final Logger logger = Logger.getLogger(QueueDispatcher.class.getName());
protected final BlockingQueue<MockResponse> responseQueue = new LinkedBlockingQueue<>();
private MockResponse failFastResponse;
@@ -31,7 +33,7 @@ public class QueueDispatcher extends Dispatcher {
// To permit interactive/browser testing, ignore requests for favicons.
final String requestLine = request.getRequestLine();
if (requestLine != null && requestLine.equals("GET /favicon.ico HTTP/1.1")) {
- System.out.println("served " + requestLine);
+ logger.info("served " + requestLine);
return new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_FOUND);
}
diff --git a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/SocketPolicy.java b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/SocketPolicy.java
index 4583621..f71f33d 100644
--- a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/SocketPolicy.java
+++ b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/SocketPolicy.java
@@ -50,6 +50,9 @@ public enum SocketPolicy {
*/
DISCONNECT_AFTER_REQUEST,
+ /** Close connection after reading half of the request body (if present). */
+ DISCONNECT_DURING_REQUEST_BODY,
+
/** Close connection after writing half of the response body (if present). */
DISCONNECT_DURING_RESPONSE_BODY,
@@ -69,7 +72,7 @@ public enum SocketPolicy {
SHUTDOWN_OUTPUT_AT_END,
/**
- * Don't response to the request but keep the socket open. For testing
+ * Don't respond to the request but keep the socket open. For testing
* read response header timeout issue.
*/
NO_RESPONSE
diff --git a/mockwebserver/src/test/java/com/squareup/okhttp/mockwebserver/MockWebServerTest.java b/mockwebserver/src/test/java/com/squareup/okhttp/mockwebserver/MockWebServerTest.java
index 0f757dd..95e0fe4 100644
--- a/mockwebserver/src/test/java/com/squareup/okhttp/mockwebserver/MockWebServerTest.java
+++ b/mockwebserver/src/test/java/com/squareup/okhttp/mockwebserver/MockWebServerTest.java
@@ -20,6 +20,7 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.OutputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
@@ -40,6 +41,7 @@ import org.junit.runners.model.Statement;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -253,7 +255,30 @@ public final class MockWebServerTest {
in.close();
}
- @Test public void disconnectHalfway() throws IOException {
+ @Test public void disconnectRequestHalfway() throws IOException {
+ server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_DURING_REQUEST_BODY));
+
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ connection.setRequestMethod("POST");
+ connection.setDoOutput(true);
+ connection.setFixedLengthStreamingMode(1024 * 1024 * 1024); // 1 GB
+ connection.connect();
+ OutputStream out = connection.getOutputStream();
+
+ byte[] data = new byte[1024 * 1024];
+ int i;
+ for (i = 0; i < 1024; i++) {
+ try {
+ out.write(data);
+ out.flush();
+ } catch (IOException e) {
+ break;
+ }
+ }
+ assertEquals(512f, i, 10f); // Halfway +/- 1%
+ }
+
+ @Test public void disconnectResponseHalfway() throws IOException {
server.enqueue(new MockResponse()
.setBody("ab")
.setSocketPolicy(SocketPolicy.DISCONNECT_DURING_RESPONSE_BODY));
@@ -333,10 +358,4 @@ public final class MockWebServerTest {
} catch (ConnectException expected) {
}
}
-
- // ANDROID-BEGIN Android uses JUnit 4.10 which does not have assertNotEquals()
- private static void assertNotEquals(Object o1, Object o2) {
- org.junit.Assert.assertFalse(o1 == o2 || (o1 != null && o1.equals(o2)));
- }
- // ANDROID-END
}