13c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/*
23c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Copyright (C) 2014 Square, Inc.
33c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
43c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License");
53c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * you may not use this file except in compliance with the License.
63c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * You may obtain a copy of the License at
73c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
83c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *      http://www.apache.org/licenses/LICENSE-2.0
93c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Unless required by applicable law or agreed to in writing, software
113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS,
123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * See the License for the specific language governing permissions and
143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * limitations under the License.
153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */
163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpackage com.squareup.okhttp.benchmarks;
173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.google.caliper.Param;
193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.google.caliper.model.ArbitraryMeasurement;
203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.google.caliper.runner.CaliperMain;
213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Protocol;
223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.internal.SslContextBuilder;
233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.mockwebserver.Dispatcher;
243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.mockwebserver.MockResponse;
253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.mockwebserver.MockWebServer;
263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.mockwebserver.RecordedRequest;
273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.ByteArrayOutputStream;
283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.IOException;
293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.OutputStream;
303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.net.URL;
313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.ArrayList;
323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.Arrays;
333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.List;
343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.Random;
353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.concurrent.TimeUnit;
363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.logging.Level;
373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.logging.Logger;
383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.zip.GZIPOutputStream;
393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport javax.net.ssl.SSLContext;
403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/**
423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * This benchmark is fake, but may be useful for certain relative comparisons.
433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * It uses a local connection to a MockWebServer to measure how many identical
443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * requests per second can be carried over a fixed number of threads.
453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */
463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpublic class Benchmark extends com.google.caliper.Benchmark {
473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private static final int NUM_REPORTS = 10;
483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private static final boolean VERBOSE = false;
493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final Random random = new Random(0);
513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** Which client to run.*/
533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Param
543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  Client client;
553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** How many concurrent requests to execute. */
573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Param({ "1", "10" })
583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  int concurrencyLevel;
593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** How many requests to enqueue to await threads to execute them. */
613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Param({ "10" })
623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  int targetBacklog;
633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** True to use TLS. */
653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  // TODO: compare different ciphers?
663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Param
673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  boolean tls;
683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** True to use gzip content-encoding for the response body. */
703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Param
713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  boolean gzip;
723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** Don't combine chunked with SPDY_3 or HTTP_2; that's not allowed. */
743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Param
753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  boolean chunked;
763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** The size of the HTTP response body, in uncompressed bytes. */
783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Param({ "128", "1048576" })
793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  int bodyByteCount;
803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** How many additional headers were included, beyond the built-in ones. */
823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Param({ "0", "20" })
833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  int headerCount;
843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** Which ALPN/NPN protocols are in use. Only useful with TLS. */
863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  List<Protocol> protocols = Arrays.asList(Protocol.HTTP_11);
873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public static void main(String[] args) {
893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    List<String> allArgs = new ArrayList<String>();
903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    allArgs.add("--instrument");
913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    allArgs.add("arbitrary");
923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    allArgs.addAll(Arrays.asList(args));
933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    CaliperMain.main(Benchmark.class, allArgs.toArray(new String[allArgs.size()]));
953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @ArbitraryMeasurement(description = "requests per second")
983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public double run() throws Exception {
993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (VERBOSE) System.out.println(toString());
1003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpClient httpClient = client.create();
1013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Prepare the client & server
1033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    httpClient.prepare(this);
1043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    MockWebServer server = startServer();
1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    URL url = server.getUrl("/");
1063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int requestCount = 0;
1083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    long reportStart = System.nanoTime();
1093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    long reportPeriod = TimeUnit.SECONDS.toNanos(1);
1103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int reports = 0;
1113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    double best = 0.0;
1123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Run until we've printed enough reports.
1143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    while (reports < NUM_REPORTS) {
1153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // Print a report if we haven't recently.
1163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      long now = System.nanoTime();
1173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      double reportDuration = now - reportStart;
1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (reportDuration > reportPeriod) {
1193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        double requestsPerSecond = requestCount / reportDuration * TimeUnit.SECONDS.toNanos(1);
1203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (VERBOSE) {
1213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          System.out.println(String.format("Requests per second: %.1f", requestsPerSecond));
1223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
1233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        best = Math.max(best, requestsPerSecond);
1243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        requestCount = 0;
1253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        reportStart = now;
1263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        reports++;
1273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
1283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // Fill the job queue with work.
1303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      while (httpClient.acceptingJobs()) {
1313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        httpClient.enqueue(url);
1323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        requestCount++;
1333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
1343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // The job queue is full. Take a break.
1363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      sleep(1);
1373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return best;
1403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Override public String toString() {
1433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    List<Object> modifiers = new ArrayList<Object>();
1443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (tls) modifiers.add("tls");
1453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (gzip) modifiers.add("gzip");
1463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (chunked) modifiers.add("chunked");
1473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    modifiers.addAll(protocols);
1483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return String.format("%s %s\nbodyByteCount=%s headerCount=%s concurrencyLevel=%s",
1503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        client, modifiers, bodyByteCount, headerCount, concurrencyLevel);
1513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private void sleep(int millis) {
1543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    try {
1553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Thread.sleep(millis);
1563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } catch (InterruptedException ignored) {
1573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private MockWebServer startServer() throws IOException {
1613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Logger.getLogger(MockWebServer.class.getName()).setLevel(Level.WARNING);
1623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    MockWebServer server = new MockWebServer();
1633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (tls) {
1653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      SSLContext sslContext = SslContextBuilder.localhost();
1663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      server.useHttps(sslContext.getSocketFactory(), false);
1673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      server.setNpnEnabled(true);
1683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      server.setNpnProtocols(protocols);
1693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    final MockResponse response = newResponse();
1723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.setDispatcher(new Dispatcher() {
1733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      @Override public MockResponse dispatch(RecordedRequest request) {
1743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return response;
1753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
1763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    });
1773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.play();
1793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return server;
1803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private MockResponse newResponse() throws IOException {
1833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    byte[] body = new byte[bodyByteCount];
1843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    random.nextBytes(body);
1853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    MockResponse result = new MockResponse();
1873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (gzip) {
1893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      body = gzip(body);
1903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.addHeader("Content-Encoding: gzip");
1913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (chunked) {
1943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.setChunkedBody(body, 1024);
1953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } else {
1963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.setBody(body);
1973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    for (int i = 0; i < headerCount; i++) {
2003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.addHeader(randomString(12), randomString(20));
2013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
2023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return result;
2043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
2053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private String randomString(int length) {
2073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String alphabet = "-abcdefghijklmnopqrstuvwxyz";
2083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    char[] result = new char[length];
2093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    for (int i = 0; i < length; i++) {
2103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result[i] = alphabet.charAt(random.nextInt(alphabet.length()));
2113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
2123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return new String(result);
2133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
2143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** Returns a gzipped copy of {@code bytes}. */
2163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private byte[] gzip(byte[] bytes) throws IOException {
2173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
2183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OutputStream gzippedOut = new GZIPOutputStream(bytesOut);
2193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzippedOut.write(bytes);
2203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzippedOut.close();
2213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return bytesOut.toByteArray();
2223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
2233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller}
224