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