diff options
author | Neil Fuller <nfuller@google.com> | 2015-04-13 13:06:22 +0100 |
---|---|---|
committer | Neil Fuller <nfuller@google.com> | 2015-04-16 11:44:31 +0100 |
commit | a2cab72aa5ff730ba2ae987b45398faafffeb505 (patch) | |
tree | 283de306182e8f1faff93d4e6515298539d8c21d /mockwebserver | |
parent | 9fa0698523b8a573b8862c0e62c533be8bd53bda (diff) | |
download | okhttp-a2cab72aa5ff730ba2ae987b45398faafffeb505.tar.gz |
Roll-up of upstream OkHttp and Okio changes
OkHttp:
From b609edd07864d7191dcda8ba1f6c833c9fe170ad
to b40f99a950cb407eff52537a97420bd253a64f63
Okio:
From 654ddf5e8f6311fda77e429c22d5e0e15f713b8d
to b5811711b141b230e4e58f577c79cfbf4c2d4028
Both "to" are head as of 20150413.
Patches applied cleanly without conflicts.
This submission will break some CTS tests due
to https://github.com/square/okhttp/issues/1552
Solutions will be made upstream and patched in.
The CTS tests broken are related to SPDY/HTTP2
which are not used by Android's embedded OkHttp.
Change-Id: I84d55b6f5c8dbc05148e86bd9421a2c393b563d4
Diffstat (limited to 'mockwebserver')
7 files changed, 133 insertions, 60 deletions
diff --git a/mockwebserver/README.md b/mockwebserver/README.md index c729668..eb698c3 100644 --- a/mockwebserver/README.md +++ b/mockwebserver/README.md @@ -116,6 +116,26 @@ assertEquals("{}", request.getUtf8Body()); By default MockWebServer uses a queue to specify a series of responses. Use a Dispatcher to handle requests using another policy. One natural policy is to dispatch on the request path. +You can, for example, filter the request instead of using `server.enqueue()`. + +```java +final Dispatcher dispatcher = new Dispatcher() { + + @Override + public MockResponse dispatch(RecordedRequest request) throws InterruptedException { + + if (request.getPath().equals("/v1/login/auth/")){ + return new MockResponse().setResponseCode(200)); + } else if (request.getPath().equals("v1/check/version/")){ + return new MockResponse().setResponseCode(200).setBody("version=9"); + } else if (request.getPath().equals("/v1/profile/info")) { + return new MockResponse().setResponseCode(200).setBody("{\\\"info\\\":{\\\"name\":\"Lucas Albuquerque\",\"age\":\"21\",\"gender\":\"male\"}}"); + } + return new MockResponse().setResponseCode(404); + } +}; +server.setDispatcher(dispatcher); +``` ### Download diff --git a/mockwebserver/pom.xml b/mockwebserver/pom.xml index ae3abb5..86d2503 100644 --- a/mockwebserver/pom.xml +++ b/mockwebserver/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>com.squareup.okhttp</groupId> <artifactId>parent</artifactId> - <version>2.3.0-SNAPSHOT</version> + <version>2.4.0-SNAPSHOT</version> </parent> <artifactId>mockwebserver</artifactId> @@ -19,6 +19,11 @@ <version>${project.version}</version> </dependency> <dependency> + <groupId>com.squareup.okhttp</groupId> + <artifactId>okhttp-ws</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> </dependency> 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 3d61d73..546d660 100644 --- a/mockwebserver/src/main/java/com/squareup/okhttp/internal/SslContextBuilder.java +++ b/mockwebserver/src/main/java/com/squareup/okhttp/internal/SslContextBuilder.java @@ -68,7 +68,7 @@ public final class SslContextBuilder { public static synchronized SSLContext localhost() { if (localhost == null) { try { - localhost = new SslContextBuilder(InetAddress.getByName(null).getHostName()).build(); + localhost = new SslContextBuilder(InetAddress.getByName("localhost").getHostName()).build(); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } catch (UnknownHostException e) { diff --git a/mockwebserver/src/main/java/com/squareup/okhttp/internal/spdy/SpdyServer.java b/mockwebserver/src/main/java/com/squareup/okhttp/internal/spdy/SpdyServer.java index fb21a08..8e93b47 100644 --- a/mockwebserver/src/main/java/com/squareup/okhttp/internal/spdy/SpdyServer.java +++ b/mockwebserver/src/main/java/com/squareup/okhttp/internal/spdy/SpdyServer.java @@ -22,10 +22,13 @@ import com.squareup.okhttp.internal.SslContextBuilder; import com.squareup.okhttp.internal.Util; import java.io.File; import java.io.IOException; +import java.net.ProtocolException; import java.net.ServerSocket; import java.net.Socket; import java.util.Arrays; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import okio.BufferedSink; @@ -34,17 +37,15 @@ import okio.Source; /** A basic SPDY/HTTP_2 server that serves the contents of a local directory. */ public final class SpdyServer implements IncomingStreamHandler { + static final Logger logger = Logger.getLogger(SpdyServer.class.getName()); + private final List<Protocol> spdyProtocols = Util.immutableList(Protocol.HTTP_2, Protocol.SPDY_3); private final File baseDirectory; - private SSLSocketFactory sslSocketFactory; - private Protocol protocol; + private final SSLSocketFactory sslSocketFactory; - public SpdyServer(File baseDirectory) { + public SpdyServer(File baseDirectory, SSLSocketFactory sslSocketFactory) { this.baseDirectory = baseDirectory; - } - - public void useHttps(SSLSocketFactory sslSocketFactory) { this.sslSocketFactory = sslSocketFactory; } @@ -53,52 +54,67 @@ public final class SpdyServer implements IncomingStreamHandler { serverSocket.setReuseAddress(true); while (true) { - Socket socket = serverSocket.accept(); - if (sslSocketFactory != null) { - socket = doSsl(socket); + Socket socket = null; + try { + socket = serverSocket.accept(); + + SSLSocket sslSocket = doSsl(socket); + String protocolString = Platform.get().getSelectedProtocol(sslSocket); + Protocol protocol = protocolString != null ? Protocol.get(protocolString) : null; + if (protocol == null || !spdyProtocols.contains(protocol)) { + throw new ProtocolException("Protocol " + protocol + " unsupported"); + } + SpdyConnection spdyConnection = new SpdyConnection.Builder(false, sslSocket) + .protocol(protocol) + .handler(this) + .build(); + spdyConnection.sendConnectionPreface(); + } catch (IOException e) { + logger.log(Level.INFO, "SpdyServer connection failure: " + e); + Util.closeQuietly(socket); + } catch (Exception e) { + logger.log(Level.WARNING, "SpdyServer unexpected failure", e); + Util.closeQuietly(socket); } - new SpdyConnection.Builder(false, socket).protocol(protocol).handler(this).build(); } } - private Socket doSsl(Socket socket) throws IOException { - SSLSocket sslSocket = - (SSLSocket) sslSocketFactory.createSocket(socket, socket.getInetAddress().getHostAddress(), - socket.getPort(), true); + private SSLSocket doSsl(Socket socket) throws IOException { + SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket( + socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true); sslSocket.setUseClientMode(false); Platform.get().configureTlsExtensions(sslSocket, null, spdyProtocols); sslSocket.startHandshake(); - String protocolString = Platform.get().getSelectedProtocol(sslSocket); - protocol = protocolString != null ? Protocol.get(protocolString) : null; - if (protocol == null || !spdyProtocols.contains(protocol)) { - throw new IllegalStateException("Protocol " + protocol + " unsupported"); - } return sslSocket; } @Override public void receive(final SpdyStream stream) throws IOException { - List<Header> requestHeaders = stream.getRequestHeaders(); - String path = null; - for (int i = 0, size = requestHeaders.size(); i < size; i++) { - if (requestHeaders.get(i).name.equals(Header.TARGET_PATH)) { - path = requestHeaders.get(i).value.utf8(); - break; + try { + List<Header> requestHeaders = stream.getRequestHeaders(); + String path = null; + for (int i = 0, size = requestHeaders.size(); i < size; i++) { + if (requestHeaders.get(i).name.equals(Header.TARGET_PATH)) { + path = requestHeaders.get(i).value.utf8(); + break; + } } - } - if (path == null) { - // TODO: send bad request error - throw new AssertionError(); - } + if (path == null) { + // TODO: send bad request error + throw new AssertionError(); + } - File file = new File(baseDirectory + path); + File file = new File(baseDirectory + path); - if (file.isDirectory()) { - serveDirectory(stream, file.list()); - } else if (file.exists()) { - serveFile(stream, file); - } else { - send404(stream, path); + if (file.isDirectory()) { + serveDirectory(stream, file.listFiles()); + } else if (file.exists()) { + serveFile(stream, file); + } else { + send404(stream, path); + } + } catch (IOException e) { + System.out.println(e.getMessage()); } } @@ -114,7 +130,7 @@ public final class SpdyServer implements IncomingStreamHandler { out.close(); } - private void serveDirectory(SpdyStream stream, String[] files) throws IOException { + private void serveDirectory(SpdyStream stream, File[] files) throws IOException { List<Header> responseHeaders = Arrays.asList( new Header(":status", "200"), new Header(":version", "HTTP/1.1"), @@ -122,8 +138,9 @@ public final class SpdyServer implements IncomingStreamHandler { ); stream.reply(responseHeaders, true); BufferedSink out = Okio.buffer(stream.getSink()); - for (String file : files) { - out.writeUtf8("<a href='" + file + "'>" + file + "</a><br>"); + for (File file : files) { + String target = file.isDirectory() ? (file.getName() + "/") : file.getName(); + out.writeUtf8("<a href='" + target + "'>" + target + "</a><br>"); } out.close(); } @@ -146,7 +163,14 @@ public final class SpdyServer implements IncomingStreamHandler { } private String contentType(File file) { - return file.getName().endsWith(".html") ? "text/html" : "text/plain"; + if (file.getName().endsWith(".css")) return "text/css"; + if (file.getName().endsWith(".gif")) return "image/gif"; + if (file.getName().endsWith(".html")) return "text/html"; + if (file.getName().endsWith(".jpeg")) return "image/jpeg"; + if (file.getName().endsWith(".jpg")) return "image/jpeg"; + if (file.getName().endsWith(".js")) return "application/javascript"; + if (file.getName().endsWith(".png")) return "image/png"; + return "text/plain"; } public static void main(String... args) throws Exception { @@ -155,8 +179,8 @@ public final class SpdyServer implements IncomingStreamHandler { return; } - SpdyServer server = new SpdyServer(new File(args[0])); - server.useHttps(SslContextBuilder.localhost().getSocketFactory()); + SpdyServer server = new SpdyServer(new File(args[0]), + SslContextBuilder.localhost().getSocketFactory()); server.run(); } } 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 8f0ee2c..09dda56 100644 --- a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockResponse.java +++ b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockResponse.java @@ -16,7 +16,7 @@ package com.squareup.okhttp.mockwebserver; import com.squareup.okhttp.Headers; -import com.squareup.okhttp.internal.ws.WebSocketListener; +import com.squareup.okhttp.ws.WebSocketListener; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; @@ -153,7 +153,7 @@ public final class MockResponse implements Cloneable { Buffer bytesOut = new Buffer(); while (!body.exhausted()) { long chunkSize = Math.min(body.size(), maxChunkSize); - bytesOut.writeUtf8(Long.toHexString(chunkSize)); + bytesOut.writeHexadecimalUnsignedLong(chunkSize); bytesOut.writeUtf8("\r\n"); bytesOut.write(body, chunkSize); bytesOut.writeUtf8("\r\n"); 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 39fbf6f..259cf3e 100644 --- a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java +++ b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java @@ -30,8 +30,8 @@ import com.squareup.okhttp.internal.spdy.IncomingStreamHandler; import com.squareup.okhttp.internal.spdy.SpdyConnection; import com.squareup.okhttp.internal.spdy.SpdyStream; import com.squareup.okhttp.internal.ws.RealWebSocket; -import com.squareup.okhttp.internal.ws.WebSocketListener; import com.squareup.okhttp.internal.ws.WebSocketProtocol; +import com.squareup.okhttp.ws.WebSocketListener; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -56,7 +56,9 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; @@ -77,6 +79,7 @@ import okio.Timeout; import static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_AT_START; import static com.squareup.okhttp.mockwebserver.SocketPolicy.FAIL_HANDSHAKE; +import static java.util.concurrent.TimeUnit.SECONDS; /** * A scriptable web server. Callers supply canned responses and the server @@ -362,6 +365,8 @@ public final class MockWebServer { } public void shutdown() throws IOException { + if (serverSocket == null) throw new IllegalStateException("shutdown() before start()"); + // Cause acceptConnections() to break out. serverSocket.close(); @@ -472,8 +477,8 @@ public final class MockWebServer { } /** - * Reads a request and writes its response. Returns true if a request was - * processed. + * Reads a request and writes its response. Returns true if further calls should be attempted + * on the socket. */ private boolean processOneRequest(Socket socket, BufferedSource source, BufferedSink sink) throws IOException, InterruptedException { @@ -503,21 +508,19 @@ public final class MockWebServer { writeHttpResponse(socket, sink, response); } + if (logger.isLoggable(Level.INFO)) { + logger.info(MockWebServer.this + " received request: " + request + + " and responded: " + response); + } + if (response.getSocketPolicy() == SocketPolicy.DISCONNECT_AT_END) { - source.close(); - sink.close(); - // Workaround for bug on Android: closing the input/output streams should close an - // SSLSocket but does not. https://code.google.com/p/android/issues/detail?id=97564 socket.close(); + return false; } else if (response.getSocketPolicy() == SocketPolicy.SHUTDOWN_INPUT_AT_END) { socket.shutdownInput(); } else if (response.getSocketPolicy() == SocketPolicy.SHUTDOWN_OUTPUT_AT_END) { socket.shutdownOutput(); } - if (logger.isLoggable(Level.INFO)) { - logger.info( - MockWebServer.this + " received request: " + request + " and responded: " + response); - } sequenceNumber++; return true; @@ -636,9 +639,15 @@ public final class MockWebServer { final WebSocketListener listener = response.getWebSocketListener(); final CountDownLatch connectionClose = new CountDownLatch(1); + + ThreadPoolExecutor replyExecutor = + new ThreadPoolExecutor(1, 1, 1, SECONDS, new LinkedBlockingDeque<Runnable>(), + Util.threadFactory(String.format("MockWebServer %s WebSocket", request.getPath()), + true)); + replyExecutor.allowCoreThreadTimeOut(true); final RealWebSocket webSocket = - new RealWebSocket(false, source, sink, new SecureRandom(), listener, - request.getPath()) { + new RealWebSocket(false /* is server */, source, sink, new SecureRandom(), replyExecutor, + listener, request.getPath()) { @Override protected void closeConnection() throws IOException { connectionClose.countDown(); } 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 388dbf6..a3816d2 100644 --- a/mockwebserver/src/test/java/com/squareup/okhttp/mockwebserver/MockWebServerTest.java +++ b/mockwebserver/src/test/java/com/squareup/okhttp/mockwebserver/MockWebServerTest.java @@ -264,4 +264,19 @@ public final class MockWebServerTest { } return headerList; } + + @Test public void shutdownWithoutStart() throws IOException { + MockWebServer server = new MockWebServer(); + try { + server.shutdown(); + fail(); + } catch (IllegalStateException expected) { + } + } + + @Test public void shutdownWithoutEnqueue() throws IOException { + MockWebServer server = new MockWebServer(); + server.start(); + server.shutdown(); + } } |