diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml index 7b8ce5278..14d93eeef 100644 --- a/benchmarks/pom.xml +++ b/benchmarks/pom.xml @@ -36,5 +36,20 @@ org.apache.httpcomponents httpclient + + io.netty + netty-transport + 4.0.15.Final + + + io.netty + netty-handler + 4.0.15.Final + + + io.netty + netty-codec-http + 4.0.15.Final + diff --git a/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/ApacheHttpClient.java b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/ApacheHttpClient.java new file mode 100644 index 000000000..cd2cba31c --- /dev/null +++ b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/ApacheHttpClient.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2014 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.benchmarks; + +import com.squareup.okhttp.internal.SslContextBuilder; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.concurrent.TimeUnit; +import java.util.zip.GZIPInputStream; +import javax.net.ssl.SSLContext; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.conn.PoolingClientConnectionManager; + +/** Benchmark Apache HTTP client. */ +class ApacheHttpClient extends SynchronousHttpClient { + private static final boolean VERBOSE = false; + + private HttpClient client; + + @Override public void prepare(Benchmark benchmark) { + super.prepare(benchmark); + ClientConnectionManager connectionManager = new PoolingClientConnectionManager(); + if (benchmark.tls) { + SSLContext sslContext = SslContextBuilder.localhost(); + connectionManager.getSchemeRegistry().register( + new Scheme("https", 443, new SSLSocketFactory(sslContext))); + } + client = new DefaultHttpClient(connectionManager); + } + + @Override public Runnable request(URL url) { + return new ApacheHttpClientRequest(url); + } + + class ApacheHttpClientRequest implements Runnable { + private final URL url; + + public ApacheHttpClientRequest(URL url) { + this.url = url; + } + + public void run() { + byte[] buffer = new byte[1024]; + long start = System.nanoTime(); + try { + HttpResponse response = client.execute(new HttpGet(url.toString())); + InputStream in = response.getEntity().getContent(); + Header contentEncoding = response.getFirstHeader("Content-Encoding"); + if (contentEncoding != null && contentEncoding.getValue().equals("gzip")) { + in = new GZIPInputStream(in); + } + + // Consume the response body. + int total = 0; + for (int count; (count = in.read(buffer)) != -1; ) { + total += count; + } + in.close(); + long finish = System.nanoTime(); + + if (VERBOSE) { + System.out.println(String.format("Transferred % 8d bytes in %4d ms", + total, TimeUnit.NANOSECONDS.toMillis(finish - start))); + } + } catch (IOException e) { + System.out.println("Failed: " + e); + } + } + } +} diff --git a/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/ApacheHttpClientRequest.java b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/ApacheHttpClientRequest.java deleted file mode 100644 index a507738df..000000000 --- a/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/ApacheHttpClientRequest.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2014 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.benchmarks; - -import java.io.IOException; -import java.io.InputStream; -import java.util.concurrent.TimeUnit; -import java.util.zip.GZIPInputStream; -import org.apache.http.Header; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; - -class ApacheHttpClientRequest implements Runnable { - private static final boolean VERBOSE = false; - private final HttpClient client; - private final String url; - - public ApacheHttpClientRequest(String url, HttpClient client) { - this.client = client; - this.url = url; - } - - public void run() { - byte[] buffer = new byte[1024]; - long start = System.nanoTime(); - try { - HttpResponse response = client.execute(new HttpGet(url)); - InputStream in = response.getEntity().getContent(); - Header contentEncoding = response.getFirstHeader("Content-Encoding"); - if (contentEncoding != null && contentEncoding.getValue().equals("gzip")) { - in = new GZIPInputStream(in); - } - - // Consume the response body. - int total = 0; - for (int count; (count = in.read(buffer)) != -1; ) { - total += count; - } - in.close(); - long finish = System.nanoTime(); - - if (VERBOSE) { - System.out.println(String.format("Transferred % 8d bytes in %4d ms", - total, TimeUnit.NANOSECONDS.toMillis(finish - start))); - } - } catch (IOException e) { - System.out.println("Failed: " + e); - } - } -} diff --git a/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/Benchmark.java b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/Benchmark.java index 3b0887af8..16bd06350 100644 --- a/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/Benchmark.java +++ b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/Benchmark.java @@ -15,10 +15,8 @@ */ package com.squareup.okhttp.benchmarks; -import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Protocol; import com.squareup.okhttp.internal.SslContextBuilder; -import com.squareup.okhttp.internal.http.HttpsURLConnectionImpl; import com.squareup.okhttp.mockwebserver.Dispatcher; import com.squareup.okhttp.mockwebserver.MockResponse; import com.squareup.okhttp.mockwebserver.MockWebServer; @@ -26,25 +24,16 @@ import com.squareup.okhttp.mockwebserver.RecordedRequest; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Random; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.GZIPOutputStream; -import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocketFactory; -import org.apache.http.client.HttpClient; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.conn.PoolingClientConnectionManager; /** * This benchmark is fake, but may be useful for certain relative comparisons. @@ -56,10 +45,10 @@ public class Benchmark { private final Random random = new Random(0); /** Which client to run.*/ - Candidate candidate = new UrlConnection(); // new OkHttp(); // new ApacheHttpClient(); + HttpClient httpClient = new NettyHttpClient(); - /** How many concurrent threads to execute. */ - int threadCount = 10; + /** How many concurrent requests to execute. */ + int concurrencyLevel = 10; /** True to use TLS. */ // TODO: compare different ciphers? @@ -80,22 +69,18 @@ public class Benchmark { /** Which ALPN/NPN protocols are in use. Only useful with TLS. */ List protocols = Arrays.asList(Protocol.HTTP_11); - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws Exception { new Benchmark().run(); } - public void run() throws IOException { - ThreadPoolExecutor executor = new ThreadPoolExecutor(threadCount, threadCount, - 1, TimeUnit.SECONDS, new LinkedBlockingQueue()); - + public void run() throws Exception { System.out.println(toString()); // Prepare the client & server - candidate.prepare(); + httpClient.prepare(this); MockWebServer server = startServer(); - String url = server.getUrl("/").toString(); + URL url = server.getUrl("/"); - int targetBacklog = 10; int requestCount = 0; long reportStart = System.nanoTime(); long reportPeriod = TimeUnit.SECONDS.toNanos(1); @@ -115,8 +100,8 @@ public class Benchmark { } // Fill the job queue with work. - while (executor.getQueue().size() < targetBacklog) { - executor.execute(candidate.request(url)); + while (httpClient.acceptingJobs()) { + httpClient.enqueue(url); requestCount++; } @@ -133,9 +118,9 @@ public class Benchmark { modifiers.addAll(protocols); return String.format("%s %s\n" - + "bodyByteCount=%s headerCount=%s threadCount=%s", - candidate.getClass().getSimpleName(), modifiers, - bodyByteCount, headerCount, threadCount); + + "bodyByteCount=%s headerCount=%s concurrencyLevel=%s", + httpClient.getClass().getSimpleName(), modifiers, + bodyByteCount, headerCount, concurrencyLevel); } private void sleep(int millis) { @@ -207,72 +192,4 @@ public class Benchmark { gzippedOut.close(); return bytesOut.toByteArray(); } - - interface Candidate { - void prepare(); - Runnable request(String url); - } - - class OkHttp implements Candidate { - private OkHttpClient client; - - @Override public void prepare() { - client = new OkHttpClient(); - client.setProtocols(protocols); - - if (tls) { - SSLContext sslContext = SslContextBuilder.localhost(); - SSLSocketFactory socketFactory = sslContext.getSocketFactory(); - HostnameVerifier hostnameVerifier = new HostnameVerifier() { - @Override public boolean verify(String s, SSLSession session) { - return true; - } - }; - client.setSslSocketFactory(socketFactory); - client.setHostnameVerifier(hostnameVerifier); - } - } - - @Override public Runnable request(String url) { - return new OkHttpRequest(client, url); - } - } - - class UrlConnection implements Candidate { - @Override public void prepare() { - if (tls) { - SSLContext sslContext = SslContextBuilder.localhost(); - SSLSocketFactory socketFactory = sslContext.getSocketFactory(); - HostnameVerifier hostnameVerifier = new HostnameVerifier() { - @Override public boolean verify(String s, SSLSession session) { - return true; - } - }; - HttpsURLConnectionImpl.setDefaultHostnameVerifier(hostnameVerifier); - HttpsURLConnectionImpl.setDefaultSSLSocketFactory(socketFactory); - } - } - - @Override public Runnable request(String url) { - return new UrlConnectionRequest(url); - } - } - - class ApacheHttpClient implements Candidate { - private HttpClient client; - - @Override public void prepare() { - ClientConnectionManager connectionManager = new PoolingClientConnectionManager(); - if (tls) { - SSLContext sslContext = SslContextBuilder.localhost(); - connectionManager.getSchemeRegistry().register( - new Scheme("https", 443, new org.apache.http.conn.ssl.SSLSocketFactory(sslContext))); - } - client = new DefaultHttpClient(connectionManager); - } - - @Override public Runnable request(String url) { - return new ApacheHttpClientRequest(url, client); - } - } } diff --git a/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/HttpClient.java b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/HttpClient.java new file mode 100644 index 000000000..136c5d86d --- /dev/null +++ b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/HttpClient.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2014 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.benchmarks; + +import java.net.URL; + +/** An HTTP client to benchmark. */ +interface HttpClient { + void prepare(Benchmark benchmark); + void enqueue(URL url) throws Exception; + boolean acceptingJobs(); +} diff --git a/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/NettyHttpClient.java b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/NettyHttpClient.java new file mode 100644 index 000000000..d5adec5a0 --- /dev/null +++ b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/NettyHttpClient.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2014 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.benchmarks; + +import com.squareup.okhttp.internal.SslContextBuilder; +import com.squareup.okhttp.internal.Util; +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpContentDecompressor; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http.LastHttpContent; +import io.netty.handler.ssl.SslHandler; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; + +/** Netty isn't an HTTP client, but it's almost one. */ +public class NettyHttpClient implements HttpClient { + private static final boolean VERBOSE = false; + + // Guarded by this. Real apps need more capable connection management. + private final List freeChannels = new ArrayList(); + private int totalChannels = 0; + + private int concurrencyLevel; + private Bootstrap bootstrap; + + @Override public void prepare(final Benchmark benchmark) { + this.concurrencyLevel = benchmark.concurrencyLevel; + + ChannelInitializer channelInitializer = new ChannelInitializer() { + @Override public void initChannel(SocketChannel channel) throws Exception { + ChannelPipeline pipeline = channel.pipeline(); + + if (benchmark.tls) { + SSLContext sslContext = SslContextBuilder.localhost(); + SSLEngine engine = sslContext.createSSLEngine(); + engine.setUseClientMode(true); + pipeline.addLast("ssl", new SslHandler(engine)); + } + + pipeline.addLast("codec", new HttpClientCodec()); + pipeline.addLast("inflater", new HttpContentDecompressor()); + pipeline.addLast("handler", new HttpChannel(channel)); + } + }; + + EventLoopGroup group = new NioEventLoopGroup(); + bootstrap = new Bootstrap(); + bootstrap.group(group) + .channel(NioSocketChannel.class) + .handler(channelInitializer); + } + + @Override public void enqueue(URL url) throws Exception { + acquireChannel(url).sendRequest(url); + } + + @Override public synchronized boolean acceptingJobs() { + int activeChannels = totalChannels - freeChannels.size(); + return activeChannels < concurrencyLevel; + } + + private HttpChannel acquireChannel(URL url) throws InterruptedException { + synchronized (this) { + if (!freeChannels.isEmpty()) { + return freeChannels.remove(freeChannels.size() - 1); + } else { + totalChannels++; + } + } + + Channel channel = bootstrap.connect(url.getHost(), Util.getEffectivePort(url)).sync().channel(); + return (HttpChannel) channel.pipeline().last(); + } + + private synchronized void release(HttpChannel httpChannel) { + freeChannels.add(httpChannel); + } + + class HttpChannel extends SimpleChannelInboundHandler { + private final SocketChannel channel; + byte[] buffer = new byte[1024]; + int total; + long start; + + public HttpChannel(SocketChannel channel) { + this.channel = channel; + } + + private void sendRequest(URL url) { + start = System.nanoTime(); + total = 0; + HttpRequest request = new DefaultFullHttpRequest( + HttpVersion.HTTP_1_1, HttpMethod.GET, url.getPath()); + request.headers().set(HttpHeaders.Names.HOST, url.getHost()); + request.headers().set(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP); + channel.writeAndFlush(request); + } + + @Override protected void channelRead0( + ChannelHandlerContext context, HttpObject message) throws Exception { + if (message instanceof HttpResponse) { + receive((HttpResponse) message); + } + if (message instanceof HttpContent) { + receive((HttpContent) message); + if (message instanceof LastHttpContent) { + release(this); + } + } + } + + @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { + super.channelInactive(ctx); + } + + void receive(HttpResponse response) { + // Don't do anything with headers. + } + + void receive(HttpContent content) { + // Consume the response body. + ByteBuf byteBuf = content.content(); + for (int toRead; (toRead = byteBuf.readableBytes()) > 0; ) { + byteBuf.readBytes(buffer, 0, Math.min(buffer.length, toRead)); + total += toRead; + } + + if (VERBOSE && content instanceof LastHttpContent) { + long finish = System.nanoTime(); + System.out.println(String.format("Transferred % 8d bytes in %4d ms", + total, TimeUnit.NANOSECONDS.toMillis(finish - start))); + } + } + + @Override public void exceptionCaught(ChannelHandlerContext context, Throwable cause) { + System.out.println("Failed: " + cause); + } + } +} diff --git a/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/OkHttp.java b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/OkHttp.java new file mode 100644 index 000000000..6fdb40e9a --- /dev/null +++ b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/OkHttp.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2014 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.benchmarks; + +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.internal.SslContextBuilder; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.concurrent.TimeUnit; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocketFactory; + +class OkHttp extends SynchronousHttpClient { + private static final boolean VERBOSE = false; + + private OkHttpClient client; + + @Override public void prepare(Benchmark benchmark) { + super.prepare(benchmark); + client = new OkHttpClient(); + client.setProtocols(benchmark.protocols); + + if (benchmark.tls) { + SSLContext sslContext = SslContextBuilder.localhost(); + SSLSocketFactory socketFactory = sslContext.getSocketFactory(); + HostnameVerifier hostnameVerifier = new HostnameVerifier() { + @Override public boolean verify(String s, SSLSession session) { + return true; + } + }; + client.setSslSocketFactory(socketFactory); + client.setHostnameVerifier(hostnameVerifier); + } + } + + @Override public Runnable request(URL url) { + return new OkHttpRequest(url); + } + + class OkHttpRequest implements Runnable { + private final URL url; + + public OkHttpRequest(URL url) { + this.url = url; + } + + public void run() { + byte[] buffer = new byte[1024]; + long start = System.nanoTime(); + try { + HttpURLConnection urlConnection = client.open(url); + InputStream in = urlConnection.getInputStream(); + + // Consume the response body. + int total = 0; + for (int count; (count = in.read(buffer)) != -1; ) { + total += count; + } + in.close(); + long finish = System.nanoTime(); + + if (VERBOSE) { + System.out.println(String.format("Transferred % 8d bytes in %4d ms", + total, TimeUnit.NANOSECONDS.toMillis(finish - start))); + } + } catch (IOException e) { + System.out.println("Failed: " + e); + } + } + } +} diff --git a/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/OkHttpRequest.java b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/OkHttpRequest.java deleted file mode 100644 index e4ab48562..000000000 --- a/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/OkHttpRequest.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2014 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.benchmarks; - -import com.squareup.okhttp.OkHttpClient; -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.concurrent.TimeUnit; - -class OkHttpRequest implements Runnable { - private static final boolean VERBOSE = false; - - private final OkHttpClient client; - private final URL url; - - public OkHttpRequest(OkHttpClient client, String url) { - try { - this.client = client; - this.url = new URL(url); - } catch (MalformedURLException e) { - throw new AssertionError(); - } - } - - public void run() { - byte[] buffer = new byte[1024]; - long start = System.nanoTime(); - try { - HttpURLConnection urlConnection = client.open(url); - InputStream in = urlConnection.getInputStream(); - - // Consume the response body. - int total = 0; - for (int count; (count = in.read(buffer)) != -1; ) { - total += count; - } - in.close(); - long finish = System.nanoTime(); - - if (VERBOSE) { - System.out.println(String.format("Transferred % 8d bytes in %4d ms", - total, TimeUnit.NANOSECONDS.toMillis(finish - start))); - } - } catch (IOException e) { - System.out.println("Failed: " + e); - } - } -} diff --git a/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/SynchronousHttpClient.java b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/SynchronousHttpClient.java new file mode 100644 index 000000000..9a0851c7f --- /dev/null +++ b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/SynchronousHttpClient.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 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.benchmarks; + +import java.net.URL; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** Any HTTP client with a blocking API. */ +abstract class SynchronousHttpClient implements HttpClient { + int targetBacklog = 10; + ThreadPoolExecutor executor; + + @Override public void prepare(Benchmark benchmark) { + executor = new ThreadPoolExecutor(benchmark.concurrencyLevel, benchmark.concurrencyLevel, + 1, TimeUnit.SECONDS, new LinkedBlockingQueue()); + } + + @Override public void enqueue(URL url) { + executor.execute(request(url)); + } + + @Override public boolean acceptingJobs() { + return executor.getQueue().size() < targetBacklog; + } + + abstract Runnable request(URL url); +} diff --git a/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/UrlConnection.java b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/UrlConnection.java new file mode 100644 index 000000000..a2c3f3af4 --- /dev/null +++ b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/UrlConnection.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2014 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.benchmarks; + +import com.squareup.okhttp.internal.SslContextBuilder; +import com.squareup.okhttp.internal.http.HttpsURLConnectionImpl; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.concurrent.TimeUnit; +import java.util.zip.GZIPInputStream; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocketFactory; + +class UrlConnection extends SynchronousHttpClient { + private static final boolean VERBOSE = false; + + @Override public void prepare(Benchmark benchmark) { + super.prepare(benchmark); + if (benchmark.tls) { + SSLContext sslContext = SslContextBuilder.localhost(); + SSLSocketFactory socketFactory = sslContext.getSocketFactory(); + HostnameVerifier hostnameVerifier = new HostnameVerifier() { + @Override public boolean verify(String s, SSLSession session) { + return true; + } + }; + HttpsURLConnectionImpl.setDefaultHostnameVerifier(hostnameVerifier); + HttpsURLConnectionImpl.setDefaultSSLSocketFactory(socketFactory); + } + } + + @Override public Runnable request(URL url) { + return new UrlConnectionRequest(url); + } + + class UrlConnectionRequest implements Runnable { + private final URL url; + + public UrlConnectionRequest(URL url) { + this.url = url; + } + + public void run() { + byte[] buffer = new byte[1024]; + long start = System.nanoTime(); + try { + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + InputStream in = urlConnection.getInputStream(); + if ("gzip".equals(urlConnection.getHeaderField("Content-Encoding"))) { + in = new GZIPInputStream(in); + } + + // Consume the response body. + int total = 0; + for (int count; (count = in.read(buffer)) != -1; ) { + total += count; + } + in.close(); + long finish = System.nanoTime(); + + if (VERBOSE) { + System.out.println(String.format("Transferred % 8d bytes in %4d ms", + total, TimeUnit.NANOSECONDS.toMillis(finish - start))); + } + } catch (IOException e) { + System.out.println("Failed: " + e); + } + } + } +} diff --git a/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/UrlConnectionRequest.java b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/UrlConnectionRequest.java deleted file mode 100644 index ab5c9b43e..000000000 --- a/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/UrlConnectionRequest.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2014 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.benchmarks; - -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.concurrent.TimeUnit; -import java.util.zip.GZIPInputStream; - -/** Uses the default java.net.HttpURLConnection implementation. */ -class UrlConnectionRequest implements Runnable { - private static final boolean VERBOSE = false; - - private final URL url; - - public UrlConnectionRequest(String url) { - try { - this.url = new URL(url); - } catch (MalformedURLException e) { - throw new AssertionError(); - } - } - - public void run() { - byte[] buffer = new byte[1024]; - long start = System.nanoTime(); - try { - HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); - InputStream in = urlConnection.getInputStream(); - if ("gzip".equals(urlConnection.getHeaderField("Content-Encoding"))) { - in = new GZIPInputStream(in); - } - - // Consume the response body. - int total = 0; - for (int count; (count = in.read(buffer)) != -1; ) { - total += count; - } - in.close(); - long finish = System.nanoTime(); - - if (VERBOSE) { - System.out.println(String.format("Transferred % 8d bytes in %4d ms", - total, TimeUnit.NANOSECONDS.toMillis(finish - start))); - } - } catch (IOException e) { - System.out.println("Failed: " + e); - } - } -}