diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml
new file mode 100644
index 000000000000..e606d7f84d12
--- /dev/null
+++ b/benchmarks/pom.xml
@@ -0,0 +1,36 @@
+
+
+
+ 4.0.0
+
+
+ com.squareup.okhttp
+ parent
+ 2.0.0-SNAPSHOT
+
+
+ benchmarks
+ Benchmarks
+
+
+
+ com.squareup.okhttp
+ okhttp
+ ${project.version}
+
+
+ com.squareup.okhttp
+ mockwebserver
+ ${project.version}
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+
+
+ org.mortbay.jetty.npn
+ npn-boot
+ provided
+
+
+
diff --git a/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/Benchmark.java b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/Benchmark.java
new file mode 100644
index 000000000000..7637a3ba2482
--- /dev/null
+++ b/benchmarks/src/main/java/com/squareup/okhttp/benchmarks/Benchmark.java
@@ -0,0 +1,238 @@
+/*
+ * 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.Protocol;
+import com.squareup.okhttp.internal.SslContextBuilder;
+import com.squareup.okhttp.mockwebserver.Dispatcher;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+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;
+
+/**
+ * This benchmark is fake, but may be useful for certain relative comparisons.
+ * It uses a local connection to a MockWebServer to measure how many identical
+ * requests per second can be carried over a fixed number of threads.
+ */
+public class Benchmark {
+ private static final int NUM_REPORTS = 10;
+ private final Random random = new Random(0);
+
+ /** Which client to run.*/
+ // TODO: implement additional candidates for other HTTP client libraries.
+ Candidate candidate = new OkHttp();
+
+ /** How many concurrent threads to execute. */
+ int threadCount = 10;
+
+ /** True to use TLS. */
+ // TODO: compare different ciphers?
+ boolean tls = false;
+
+ /** True to use gzip content-encoding for the response body. */
+ boolean gzip = true;
+
+ /** Don't combine chunked with SPDY_3 or HTTP_2; that's not allowed. */
+ boolean chunked = true;
+
+ /** The size of the HTTP response body, in uncompressed bytes. */
+ int bodyByteCount = 1024 * 1024;
+
+ /** How many additional headers were included, beyond the built-in ones. */
+ int headerCount = 20;
+
+ /** 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 {
+ new Benchmark().run();
+ }
+
+ public void run() throws IOException {
+ ThreadPoolExecutor executor = new ThreadPoolExecutor(threadCount, threadCount,
+ 1, TimeUnit.SECONDS, new LinkedBlockingQueue());
+
+ System.out.println(toString());
+
+ // Prepare the client & server
+ candidate.prepare();
+ MockWebServer server = startServer();
+ String url = server.getUrl("/").toString();
+
+ int targetBacklog = 10;
+ int requestCount = 0;
+ long reportStart = System.nanoTime();
+ long reportPeriod = TimeUnit.SECONDS.toNanos(1);
+ int reports = 0;
+
+ // Run until we've printed enough reports.
+ while (reports < NUM_REPORTS) {
+ // Print a report if we haven't recently.
+ long now = System.nanoTime();
+ double reportDuration = now - reportStart;
+ if (reportDuration > reportPeriod) {
+ double requestsPerSecond = requestCount / reportDuration * TimeUnit.SECONDS.toNanos(1);
+ System.out.println(String.format("Requests per second: %.1f", requestsPerSecond));
+ requestCount = 0;
+ reportStart = now;
+ reports++;
+ }
+
+ // Fill the job queue with work.
+ while (executor.getQueue().size() < targetBacklog) {
+ executor.execute(candidate.request(url));
+ requestCount++;
+ }
+
+ // The job queue is full. Take a break.
+ sleep(10);
+ }
+ }
+
+ @Override public String toString() {
+ List